Wallet actions
Once your app is wrapped in OpenfortProvider
and the user has an embedded wallet, you can send transactions through the smart account.
Choose your preferred approach:
- Using wagmi hooks (recommended) - Use wagmi's built-in hooks for better state management and type safety
- Switch to use provider directly - Use the wallet client directly without additional dependencies
This page shows the wagmi approach. Use the helper below as a minimal starting point for single-call transfers.
Send an ERC-20 transfer
import { useCallback } from 'react';
import { parseUnits } from 'viem';
import { useAccount, useWriteContract, useWaitForTransactionReceipt } from 'wagmi';
const usdcAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; // replace with your token
const usdcAbi = [{
name: 'transfer',
type: 'function',
stateMutability: 'nonpayable',
inputs: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
outputs: [{ name: 'success', type: 'bool' }],
}] as const;
export function useSendUsdc() {
const { address } = useAccount();
const { writeContract, data: hash, isPending, error } = useWriteContract();
const { isLoading: isConfirming, isSuccess: isConfirmed } =
useWaitForTransactionReceipt({ hash });
const sendUsdc = useCallback(async (to: `0x${string}`, amount: string) => {
if (!address) throw new Error('Wallet not connected');
const units = parseUnits(amount, 6);
writeContract({
address: usdcAddress,
abi: usdcAbi,
functionName: 'transfer',
args: [to, units],
});
}, [address, writeContract]);
return {
sendUsdc,
hash,
isPending,
isConfirming,
isConfirmed,
error,
};
}
Use the hook in your component like this:
import { useSendUsdc } from './useSendUsdc';
export function TransferButton() {
const { sendUsdc, isPending, isConfirming, isConfirmed, error } = useSendUsdc();
const handleTransfer = () => {
sendUsdc('0x742d35Cc6634C0532925a3b8D24ec3Ad3E5b6ca', '10.5');
};
return (
<div>
<button
onClick={handleTransfer}
disabled={isPending || isConfirming}
>
{isPending ? 'Preparing...' : isConfirming ? 'Confirming...' : 'Send USDC'}
</button>
{isConfirmed && <p>Transfer confirmed!</p>}
{error && <p>Error: {error.message}</p>}
</div>
);
}
For multi-step flows (e.g. ERC-20 approvals before deposits) follow the composition in the recipes mentioned above.
Sponsor gas from the dashboard
Keep the experience gasless—or recover costs in ERC-20 tokens—by attaching a gas policy created in the Openfort dashboard.
- Navigate to Dashboard → Sponsor policies, create a policy, and copy the generated
pol_...
identifier. - Configure match rules (chain, method, contract) so only the intended
wallet_sendCalls
bundles qualify. - Choose Pay gas for user to sponsor execution or Charge dynamic/fixed amount of ERC-20 to charge users before the transaction is sent.
Wire the policy into ethereumProviderPolicyId
inside walletConfig
so the SDK appends it automatically.
const config = createConfig(
getDefaultConfig({
appName: "Openfort demo",
chains: [polygonAmoy],
ssr: true,
})
);
export function Providers({ children }: { children: React.ReactNode }) {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<OpenfortProvider
publishableKey={"YOUR_OPENFORT_PUBLISHABLE_KEY"}
walletConfig={{
shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
createEncryptedSessionEndpoint: "YOUR_BACKEND_ENDPOINT",
ethereumProviderPolicyId: {
[polygonAmoy.id]: 'pol_...',
},
}}
>
{children}
</OpenfortProvider>
</QueryClientProvider>
</WagmiProvider>
);
}
Dashboard edits apply immediately—matching wallet_sendCalls
bundles will respect the latest sponsorship rules without redeploying your frontend.
More examples
useAaveOperations.ts
– chains approvals with lending transactions.useVaultOperations.ts
– batches deposits and redemptions.