import { FC, useState } from 'react';
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { isTempoWallet } from '@dynamic-labs/tempo';
import { encodeFunctionData, erc20Abi, Hex } from 'viem';
const GaslessTransactionButton: FC = () => {
const { primaryWallet } = useDynamicContext();
const [signedTx, setSignedTx] = useState<string | null>(null);
const onCreateGaslessTx = async () => {
if (!primaryWallet || !isTempoWallet(primaryWallet)) {
throw new Error('Tempo wallet not found');
}
const publicClient = await primaryWallet.getPublicClient();
const walletClient = await primaryWallet.getWalletClient();
const fromAddress = primaryWallet.address as Hex;
const toAddress = '0xRecipient...' as Hex;
const tokenAddress = '0xToken...' as Hex;
const amount = BigInt(1000000);
// Encode transfer
const callData = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: [toAddress, amount],
});
// Get nonce
const nonce = await publicClient.getTransactionCount({
address: fromAddress,
});
const gasPrice = await publicClient.getGasPrice();
const chainId = await publicClient.getChainId();
// Set validity window (valid for next 10 minutes)
const now = Math.floor(Date.now() / 1000);
const validAfter = now;
const validBefore = now + 600; // 10 minutes
// Build transaction with fee sponsorship flag
const tempoTx = {
calls: [
{
data: callData,
to: tokenAddress,
value: BigInt(0),
},
],
chainId,
gas: BigInt(300000),
maxFeePerGas: gasPrice,
maxPriorityFeePerGas: gasPrice / BigInt(10),
nonce,
// Fee sponsorship flags
awaitingFeePayer: true,
validAfter,
validBefore,
};
// Sign the transaction (but don't broadcast)
const signature = await walletClient.signTransaction(tempoTx);
setSignedTx(signature);
console.log('Signed transaction (awaiting fee payer):', signature);
// In production, send this to your fee payer service:
// await fetch('/api/sponsor-transaction', {
// method: 'POST',
// body: JSON.stringify({ signedTransaction: signature }),
// });
};
return (
<div>
<button onClick={onCreateGaslessTx}>Create Gasless Transaction</button>
{signedTx && (
<div>
<p>Signed transaction ready for sponsorship:</p>
<code style={{ wordBreak: 'break-all', fontSize: '12px' }}>
{signedTx}
</code>
</div>
)}
</div>
);
};