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:
- Switch to use wagmi hooks (recommended) - Use wagmi's built-in hooks for better state management and type safety
- Using provider directly - Use the wallet client directly without additional dependencies
This page shows the direct provider approach using the wallet_sendCalls
RPC method directly through the wallet client.
Send an ERC-20 transfer
import { useCallback } from 'react';
import { encodeFunctionData, parseUnits } from 'viem';
import { baseSepolia } from 'wagmi/chains';
import { useWalletClient } 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' }],
}];
export function useSendUsdc() {
const { data: walletClient } = useWalletClient();
return useCallback(async (to: `0x${string}`, amount: string) => {
if (!walletClient || !walletClient.account) throw new Error('Wallet client not ready');
const units = parseUnits(amount, 6);
const data = encodeFunctionData({ abi: usdcAbi, functionName: 'transfer', args: [to, units] });
const chainIdHex = `0x${(walletClient.chain?.id ?? baseSepolia.id).toString(16)}`;
return await walletClient.request({
method: 'wallet_sendCalls',
params: [{
version: '1.0',
chainId: chainIdHex,
from: walletClient.account.address,
calls: [{ to: usdcAddress, value: '0x0', data }],
}],
});
}, [walletClient]);
}
Use the hook in your component like this:
import { useState } from 'react';
import { useSendUsdc } from './useSendUsdc';
export function TransferButton() {
const [isLoading, setIsLoading] = useState(false);
const [txHash, setTxHash] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const sendUsdc = useSendUsdc();
const handleTransfer = async () => {
try {
setIsLoading(true);
setError(null);
const result = await sendUsdc('0x742d35Cc6634C0532925a3b8D24ec3Ad3E5b6ca', '10.5');
setTxHash(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'Transfer failed');
} finally {
setIsLoading(false);
}
};
return (
<div>
<button onClick={handleTransfer} disabled={isLoading}>
{isLoading ? 'Sending...' : 'Send USDC'}
</button>
{txHash && <p>Transaction: {txHash}</p>}
{error && <p>Error: {error}</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.