Privy Integration (AVAX)
Privy handles login/signature UX, SmoothSend handles sponsorship + UserOperation submission.
1) Root providers
Configure both Privy and SmoothSend once at app root.
RootProviders.tsx
TypeScript (React)
1import { PrivyProvider } from '@privy-io/react-auth';2import { avalancheFuji } from 'viem/chains';3import { SmoothSendAvaxProvider } from '@smoothsend/sdk/avax';4 5export function RootProviders({ children }: { children: React.ReactNode }) {6 return (7 <PrivyProvider8 appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID!}9 config={{10 embeddedWallets: { ethereum: { createOnLogin: 'users-without-wallets' } },11 defaultChain: avalancheFuji,12 supportedChains: [avalancheFuji],13 }}14 >15 <SmoothSendAvaxProvider16 apiKey={process.env.NEXT_PUBLIC_SMOOTHSEND_API_KEY!} // pk_nogas_*17 // network is optional; defaults to "testnet"18 // network="mainnet" // uncomment for mainnet19 >20 {children}21 </SmoothSendAvaxProvider>22 </PrivyProvider>23 );24}2) One hook in your component
Keep a wagmi-like write flow with Privy signatures. Do not pass
mode to the hook.PrivyGaslessButton.tsx
TypeScript (React)
1import { usePublicClient } from 'wagmi';2import { usePrivy, useSignMessage } from '@privy-io/react-auth';3import { useSmoothSendPrivyWrite } from '@smoothsend/sdk/avax';4 5function PrivyGaslessButton() {6 const publicClient = usePublicClient();7 const { user } = usePrivy();8 const { signMessage } = useSignMessage();9 10 const ownerAddress = user?.wallet?.address as `0x${string}` | undefined;11 12 // Every writeContract call below goes through SmoothSend sponsorship infra.13 // mode is set per writeContract call (not in hook params).14 const { writeContract, isPending } = useSmoothSendPrivyWrite({15 publicClient,16 ownerAddress: ownerAddress ?? '0x0000000000000000000000000000000000000000',17 // optional override if provider-level network is not set:18 // network: 'mainnet',19 signMessage: async ({ message }) => (await signMessage({ message })).signature,20 });21 22 return (23 <button24 disabled={isPending}25 onClick={() =>26 writeContract({27 address: '0xYourContract',28 abi: YOUR_ABI,29 functionName: 'mint',30 args: [1],31 mode: 'developer-sponsored',32 })33 }34 >35 {isPending ? 'Sponsoring...' : 'Submit Gasless'}36 </button>37 );38}3) Use user-pays-erc20 mode (Privy)
Set mode on the write call itself. This is where Privy users switch billing behavior. If
mode is omitted, default is developer-sponsored.PrivyUserPays.tsx
TypeScript (React)
1await writeContract({2 address: '0xYourContract',3 abi: YOUR_ABI,4 functionName: 'mint',5 args: [1],6 mode: 'user-pays-erc20', // put mode here (per transaction)7 paymaster: {8 token: '0xYourErc20Token',9 receiver: '0xYourTreasuryAddress',10 },11});Notes
Set apiKey on SmoothSendAvaxProvider. network is optional and defaults to "testnet".
Ensure ownerAddress matches the exact Privy wallet used to sign.
For Privy flow, mode is set on each writeContract(...) call, not on useSmoothSendPrivyWrite(...).
If you do not provide mode, SmoothSend uses developer-sponsored by default.
Owner/admin-gated contracts may need direct EOA writes if ownership is not set to the smart account.