# Send transaction

Send onchain transactions from your user's embedded wallet.

## Send a native transfer

Use wagmi's `useSendTransaction` to send native ETH/MATIC transfers:

```tsx [SendNative.tsx]
import { useSendTransaction, useWaitForTransactionReceipt } from 'wagmi'
import { parseEther } from 'viem'

function SendNative() {
  const { sendTransaction, data: hash, isPending, error } = useSendTransaction()
  const { isLoading: isConfirming, isSuccess: isConfirmed } =
    useWaitForTransactionReceipt({ hash })

  const handleSend = () => {
    sendTransaction({
      to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
      value: parseEther('0.01'),
    })
  }

  return (
    <div>
      <button onClick={handleSend} disabled={isPending || isConfirming}>
        {isPending ? 'Preparing...' : isConfirming ? 'Confirming...' : 'Send ETH'}
      </button>
      {isConfirmed && <p>Transfer confirmed!</p>}
      {error && <p>Error: {error.message}</p>}
    </div>
  )
}
```

## Send a contract call

Use wagmi's `useWriteContract` for ERC-20 transfers or any contract interaction:

:::info
This example uses a testnet USDC token address for demonstration purposes. This is not the real mainnet USDC token.
:::

:::code-group
```tsx [useSendUsdc.ts]
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,
  }
}
```

```tsx [TransferButton.tsx]
import { useSendUsdc } from './useSendUsdc'

export function TransferButton() {
  const { sendUsdc, isPending, isConfirming, isConfirmed, error } = useSendUsdc()

  const handleTransfer = () => {
    sendUsdc('0x742d35Cc6634C0532925a3b844Bc454e4438f44e', '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>
  )
}
```
:::

## Gas sponsorship

:::warning
Gas sponsorship requires a **Smart Account** or **Delegated Account**. EOA wallets pay their own gas and cannot use gas sponsorship policies. Make sure `accountType` is set to `AccountTypeEnum.SMART_ACCOUNT` or `AccountTypeEnum.DELEGATED_ACCOUNT` in your [wallet configuration](/docs/products/embedded-wallet/react/wallet/ethereum#setting-the-account-type).
:::

:::info
For full details on gas sponsorship policies, see [Gas sponsorship](/docs/configuration/gas-sponsorship).
:::

Keep the experience gasless by attaching a gas policy created in the Openfort dashboard:

1. Navigate to [**Dashboard → Gas sponsorship**](https://dashboard.openfort.io/policies), add a gas sponsorship, and copy the generated `pol_...` identifier.
2. Configure match rules (chain, method, contract) so only the intended `wallet_sendCalls` bundles qualify.
3. Pick a **Sponsorship mode**: **App pays** covers gas so users transact for free, or **User pays** lets users pay gas in a token you choose. On EVM you select a network and a gas-payment token — supported stablecoins (USDC, USDT, DAI, WETH) are priced automatically, or you set a fixed amount per transaction.

:::info
This applies to Ethereum wallets. For Solana, see [Send transaction (Solana)](/docs/products/embedded-wallet/react/wallet/actions/send-transaction/solana#gasless-solana-transactions).
:::

Wire the policy into `ethereumFeeSponsorshipId` inside `walletConfig.ethereum` so the SDK appends it automatically:

```tsx [Providers.tsx]
const config = createConfig(
  getDefaultConfig({
    appName: "Openfort demo",
    chains: [polygonAmoy],
    ssr: true,
  })
)

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>
      <WagmiProvider config={config}>
        <OpenfortWagmiBridge>
          <OpenfortProvider
            publishableKey={"YOUR_OPENFORT_PUBLISHABLE_KEY"}
            walletConfig={{
              shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
              createEncryptedSessionEndpoint: "YOUR_BACKEND_ENDPOINT",
              ethereum: {
                chainId: polygonAmoy.id,
                ethereumFeeSponsorshipId: {
                  [polygonAmoy.id]: 'pol_...',
                },
              },
            }}
          >
            {children}
          </OpenfortProvider>
        </OpenfortWagmiBridge>
      </WagmiProvider>
    </QueryClientProvider>
  )
}
```

Dashboard edits apply immediately, matching `wallet_sendCalls` bundles will respect the latest sponsorship rules without redeploying your frontend.

## More examples

* [`useAaveOperations.ts`](https://github.com/openfort-xyz/recipes-hub/tree/main/aave/frontend/src/hooks/useAaveOperations.ts) chains approvals with lending transactions.
* [`useVaultOperations.ts`](https://github.com/openfort-xyz/recipes-hub/tree/main/morpho/frontend/src/hooks/useVaultOperations.ts) batches deposits and redemptions.
