> Feedback: If these docs are stale, missing, or confusing, post sanitized feedback to `https://docs.tempo.xyz/api/feedback` with `source: "mcp"`, a short `message`, and any relevant `toolName`, `relatedResource`, or `client`.
# Send a Payment

Send stablecoin payments between accounts on Tempo. Payments can include optional memos for reconciliation and tracking.

:::warning\[Confirm delivery, not just transaction success]
On post-T6 networks, a blocked TIP-20 `transfer` / `transferFrom` still succeeds, but credits `ReceivePolicyGuard` at `0xB10C000000000000000000000000000000000000` instead of the intended receiver.

* Before marking a payment, withdrawal, or deposit delivered, confirm the `Transfer` recipient is the intended receiver or its TIP-1022 master.
* Index `ReceivePolicyGuard.TransferBlocked` so redirected funds can be surfaced and claimed later.

See [Configure Receive Policies](/docs/guide/payments/configure-receive-policies).
:::

## Demo

By the end of this guide you will be able to send payments on Tempo with an optional memo.

<Demo.Container name="Send a Payment" footerVariant="source" src="tempoxyz/examples/tree/main/examples/payments">
  <Connect stepNumber={1} />

  <AddFunds stepNumber={2} />

  <SendPayment stepNumber={3} last />
</Demo.Container>

## Steps

::::steps

### Set up Wagmi

Ensure that you have set up your project with Wagmi, a Tempo chain config, and a wallet connector:

* [Connection details](/docs/quickstart/connection-details)
* [TypeScript SDK](/docs/sdk/typescript)
* [Wallet integration](/docs/quickstart/wallet-developers)

### Add testnet funds¹

Before you can send a payment, you need to fund your account. In this guide you will be sending `AlphaUSD` (`0x20c000…0001`).

The built-in Tempo testnet faucet funds accounts with `AlphaUSD`.

<Demo.Container name="Add Funds" footerVariant="balances" showBadge={false} tokens={[Demo.alphaUsd]}>
  <AddFunds stepNumber={1} last />
</Demo.Container>

:::code-group

```tsx twoslash [AddFunds.ts]
// @noErrors
import { Hooks } from 'wagmi/tempo'
import { useConnection } from 'wagmi'

function AddFunds() {
  const { address } = useConnection()
  const { mutate, isPending } = Hooks.faucet.useFundSync()

  return (
    <button onClick={() => mutate({ account: address })} disabled={isPending}>
      Add Funds
    </button>
  )
}
```

```tsx twoslash [wagmi.config.ts] filename="wagmi.config.ts"
// @noErrors
// [!include ~/snippets/wagmi.config.ts:setup]
```

:::

:::warning
¹ It is important to note that the `addFunds` Hook only works on testnets as a convenience feature to get
started quickly. For production, you will need to onramp & fund your account manually.
:::

### Add send payment logic

Now that you have `AlphaUSD` you are ready to add logic to send a payment with an optional memo.

<Demo.Container name="Send Payment" footerVariant="balances" showBadge={false} tokens={[Demo.alphaUsd]}>
  <AddFunds stepNumber={1} />

  <SendPayment stepNumber={2} last />
</Demo.Container>

:::code-group

```tsx twoslash [SendPaymentWithMemo.tsx]
import { Hooks } from 'wagmi/tempo'
import { parseUnits, stringToHex, pad } from 'viem'

// @noErrors
function SendPaymentWithMemo() {
  const sendPayment = Hooks.token.useTransferSync() // [!code hl]

  return (
    <form onSubmit={
      (event) => {
        event.preventDefault()
        const formData = new FormData(event.target as HTMLFormElement)

        const recipient = (formData.get('recipient') ||
          '0x0000000000000000000000000000000000000000') as `0x${string}`
        const memo = (formData.get('memo') || '') as string

        sendPayment.mutate({ // [!code hl]
          amount: parseUnits('100', 6), // [!code hl]
          to: recipient, // [!code hl]
          token: '0x20c0000000000000000000000000000000000001', // [!code hl]
          memo: memo ? pad(stringToHex(memo), { size: 32 }) : undefined, // [!code hl]
        }) // [!code hl]
      }
    }>
      <div>
        <label htmlFor="recipient">Recipient address</label>
        <input type="text" name="recipient" placeholder="0x..." />
      </div>
      <div>
        <label htmlFor="memo">Memo (optional)</label>
        <input type="text" name="memo" placeholder="Optional memo" />
      </div>
      <button // [!code hl]
        type="submit" // [!code hl]
        disabled={sendPayment.isPending} // [!code hl]
      > {/* [!code hl] */}
        Send Payment {/* [!code hl] */}
      </button> {/* [!code hl] */}
    </form>
  )
}
```

```tsx twoslash [wagmi.config.ts] filename="wagmi.config.ts"
// @noErrors
// [!include ~/snippets/wagmi.config.ts:setup]
```

:::

### Display receipt

Now that you can send a payment, you can display the transaction receipt on success.

:::code-group

```tsx twoslash [SendPaymentWithMemo.tsx]
import { Hooks } from 'wagmi/tempo'
import { parseUnits, stringToHex, pad } from 'viem'

// @noErrors
function SendPaymentWithMemo() {
  const sendPayment = Hooks.token.useTransferSync()

  return (
    <>
      {/* ... your payment form ... */}
      {sendPayment.data && ( // [!code ++]
        <a href={`https://explore.tempo.xyz/tx/${sendPayment.data.receipt.transactionHash}`}> {/* [!code ++] */}
          View receipt {/* [!code ++] */}
        </a> {/* [!code ++] */}
      )} {/* [!code ++] */}
    </>
  )
}
```

```tsx twoslash [wagmi.config.ts] filename="wagmi.config.ts"
// @noErrors
// [!include ~/snippets/wagmi.config.ts:setup]
```

:::

### Next steps

Now that you have made a payment you can

* **[Accept a payment](/docs/guide/payments/accept-a-payment)** to receive payments in your application
* Learn about [Tempo Transactions](/docs/protocol/transactions) for batching, sponsorship, scheduling, and more
* Send a payment [with a specific fee token](/docs/guide/payments/pay-fees-in-any-stablecoin)
  ::::

## Recipes

### Basic transfer

Send a payment using the standard `transfer` function:

<Tabs stateKey="library">
  <Tab title="Viem">
    :::code-group

    ```ts twoslash [example.ts]
    // @noErrors
    import { parseUnits } from 'viem'
    import { client } from './viem.config'

    const { receipt } = await client.token.transferSync({
      amount: parseUnits('100', 6), // 100 tokens (6 decimals) // [!code hl]
      to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb', // [!code hl]
      token: '0x20c0000000000000000000000000000000000001', // AlphaUSD // [!code hl]
    })
    ```

    ```ts twoslash [viem.config.ts]
    // [!include ~/snippets/viem.config.ts:setup]
    ```

    :::
  </Tab>

  <Tab title="Wagmi">
    ```tsx twoslash
    // @noErrors
    import { Hooks } from 'wagmi/tempo'
    import { parseUnits } from 'viem'

    function SendPayment() {
      const { mutate, isPending } = Hooks.token.useTransferSync() // [!code hl]

      return (
        <button
          onClick={() =>
            mutate({ // [!code hl]
              amount: parseUnits('100', 6), // [!code hl]
              to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb', // [!code hl]
              token: '0x20c0000000000000000000000000000000000001', // [!code hl]
            }) // [!code hl]
          }
          disabled={isPending}
        >
          Send Payment
        </button>
      )
    }
    ```
  </Tab>

  <Tab title="Rust">
    :::code-group

    ```rust [example.rs]
    use alloy::{
        primitives::{address, U256},
        providers::ProviderBuilder,
    };
    use tempo_alloy::{TempoNetwork, contracts::precompiles::ITIP20};

    mod provider;

    #[tokio::main]
    async fn main() -> Result<(), Box<dyn std::error::Error>> {
        let provider = provider::get_provider().await?;

        let token = ITIP20::new( // [!code hl]
            address!("0x20c0000000000000000000000000000000000001"), // AlphaUSD // [!code hl]
            &provider, // [!code hl]
        ); // [!code hl]

        let receipt = token // [!code hl]
            .transfer( // [!code hl]
                address!("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb"), // [!code hl]
                U256::from(100_000_000), // 100 tokens (6 decimals) // [!code hl]
            ) // [!code hl]
            .send() // [!code hl]
            .await? // [!code hl]
            .get_receipt() // [!code hl]
            .await?; // [!code hl]

        println!("Transfer successful: {:?}", receipt.transaction_hash);

        Ok(())
    }
    ```

    ```rust [provider.rs]
    // [!include ~/snippets/rust-signer-provider.rs:setup]
    ```

    :::
  </Tab>

  <Tab title="Python">
    :::code-group

    ```python [example.py]
    from eth_abi import encode
    from pytempo import Call, TempoTransaction
    from provider import w3, account

    TRANSFER_SELECTOR = bytes.fromhex("a9059cbb")
    ALPHA_USD = "0x20c0000000000000000000000000000000000001"

    data = TRANSFER_SELECTOR + encode( # [!code hl]
        ["address", "uint256"], # [!code hl]
        ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb", 100_000_000], # [!code hl]
    ) # [!code hl]

    tx = TempoTransaction.create(
        chain_id=w3.eth.chain_id,
        gas_limit=100_000,
        max_fee_per_gas=w3.eth.gas_price * 2,
        max_priority_fee_per_gas=w3.eth.gas_price,
        nonce=w3.eth.get_transaction_count(account.address),
        calls=(
            Call.create(to=ALPHA_USD, data="0x" + data.hex()), # [!code hl]
        ),
    )

    signed_tx = tx.sign(account.key.hex())
    tx_hash = w3.eth.send_raw_transaction(signed_tx.encode())
    ```

    ```python [provider.py]
    from web3 import Web3
    from eth_account import Account

    w3 = Web3(Web3.HTTPProvider("https://rpc.tempo.xyz"))
    account = Account.from_key("0x...")
    ```

    :::
  </Tab>

  <Tab title="Go">
    :::code-group

    ```go [main.go]
    package main

    import (
    	"context"
    	"log"
    	"math/big"

    	"github.com/ethereum/go-ethereum/common"
    	"github.com/tempoxyz/tempo-go/pkg/signer"
    	"github.com/tempoxyz/tempo-go/pkg/transaction"
    )

    func buildTransferData(to common.Address, amount *big.Int) []byte {
    	data := make([]byte, 68) // 4 (selector) + 32 (address) + 32 (uint256)
    	data[0], data[1], data[2], data[3] = 0xa9, 0x05, 0x9c, 0xbb
    	copy(data[16:36], to.Bytes())
    	amount.FillBytes(data[36:68])
    	return data
    }

    func main() {
    	sgn, _ := signer.NewSigner("0x...")
    	c := newClient()
    	ctx := context.Background()

    	nonce, _ := c.GetTransactionCount(ctx, sgn.Address().Hex())

    	recipient := common.HexToAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb")
    	alphaUSD := common.HexToAddress("0x20c0000000000000000000000000000000000001")

    	tx := transaction.NewBuilder(big.NewInt(transaction.ChainIdMainnet)).
    		SetNonce(nonce).
    		SetGas(100_000).
    		SetMaxFeePerGas(big.NewInt(25_000_000_000)).
    		SetMaxPriorityFeePerGas(big.NewInt(1_000_000_000)).
    		AddCall( // [!code hl]
    			alphaUSD, // [!code hl]
    			big.NewInt(0), // [!code hl]
    			buildTransferData(recipient, big.NewInt(100_000_000)), // [!code hl]
    		). // [!code hl]
    		Build()

    	_ = transaction.SignTransaction(tx, sgn)
    	serialized, _ := transaction.Serialize(tx, nil)
    	txHash, _ := c.SendRawTransaction(ctx, serialized)

    	log.Printf("Transaction hash: %s", txHash)
    }
    ```

    ```go [provider.go]
    // [!include ~/snippets/go-provider.go:setup]
    ```

    :::
  </Tab>

  <Tab title="Cast">
    ```bash
    $ cast erc20 transfer \
      0x20c0000000000000000000000000000000000001 \
      0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb \
      100000000 \
      --rpc-url $TEMPO_RPC_URL \
      --private-key $PRIVATE_KEY # [!code hl]
    ```
  </Tab>

  <Tab title="Solidity">
    ```solidity
    import {ITIP20} from "tempo-std/interfaces/ITIP20.sol";

    contract PaymentSender {
        ITIP20 public token;

        function sendPayment(address recipient, uint256 amount) external {
            token.transfer(recipient, amount); // [!code hl]
        }
    }
    ```
  </Tab>
</Tabs>

### Transfer with memo

Include a memo for payment reconciliation and tracking. The memo is a 32-byte value that can store payment references, invoice IDs, order numbers, or any other metadata.

<Tabs stateKey="library">
  <Tab title="Viem">
    :::code-group

    ```ts twoslash [example.ts]
    // @noErrors
    import { parseUnits, stringToHex, pad } from 'viem'
    import { client } from './viem.config'

    const invoiceId = pad(stringToHex('INV-12345'), { size: 32 }) // [!code hl]

    const { receipt } = await client.token.transferSync({
      amount: parseUnits('100', 6),
      to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb',
      token: '0x20c0000000000000000000000000000000000001',
      memo: invoiceId, // [!code hl]
    })
    ```

    ```ts twoslash [viem.config.ts]
    // [!include ~/snippets/viem.config.ts:setup]
    ```

    :::
  </Tab>

  <Tab title="Wagmi">
    ```tsx twoslash
    // @noErrors
    import { Hooks } from 'wagmi/tempo'
    import { parseUnits, stringToHex, pad } from 'viem'

    function SendPaymentWithMemo() {
      const { mutate, isPending } = Hooks.token.useTransferSync()

      return (
        <button
          onClick={() =>
            mutate({
              amount: parseUnits('100', 6),
              to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb',
              token: '0x20c0000000000000000000000000000000000001',
              memo: pad(stringToHex('INV-12345'), { size: 32 }), // [!code hl]
            })
          }
          disabled={isPending}
        >
          Send Payment
        </button>
      )
    }
    ```
  </Tab>

  <Tab title="Rust">
    :::code-group

    ```rust [example.rs]
    use alloy::{
        primitives::{address, B256, U256},
        providers::ProviderBuilder,
    };
    use tempo_alloy::{TempoNetwork, contracts::precompiles::ITIP20};

    mod provider;

    #[tokio::main]
    async fn main() -> Result<(), Box<dyn std::error::Error>> {
        let provider = provider::get_provider().await?;

        let token = ITIP20::new(
            address!("0x20c0000000000000000000000000000000000001"),
            &provider,
        );

        let receipt = token
            .transferWithMemo( // [!code hl]
                address!("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb"),
                U256::from(100_000_000),
                B256::left_padding_from("INV-12345".as_bytes()), // [!code hl]
            )
            .send()
            .await?
            .get_receipt()
            .await?;

        println!("Transfer successful: {:?}", receipt.transaction_hash);

        Ok(())
    }
    ```

    ```rust [provider.rs]
    // [!include ~/snippets/rust-signer-provider.rs:setup]
    ```

    :::
  </Tab>

  <Tab title="Python">
    :::code-group

    ```python [example.py]
    from eth_abi import encode
    from pytempo import Call, TempoTransaction
    from provider import w3, account

    TRANSFER_WITH_MEMO_SELECTOR = bytes.fromhex("76a8ee59")
    ALPHA_USD = "0x20c0000000000000000000000000000000000001"

    memo = b"INV-12345" + b"\x00" * 23  # right-pad to 32 bytes # [!code hl]

    data = TRANSFER_WITH_MEMO_SELECTOR + encode( # [!code hl]
        ["address", "uint256", "bytes32"], # [!code hl]
        ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb", 100_000_000, memo], # [!code hl]
    ) # [!code hl]

    tx = TempoTransaction.create(
        chain_id=w3.eth.chain_id,
        gas_limit=100_000,
        max_fee_per_gas=w3.eth.gas_price * 2,
        max_priority_fee_per_gas=w3.eth.gas_price,
        nonce=w3.eth.get_transaction_count(account.address),
        calls=(
            Call.create(to=ALPHA_USD, data="0x" + data.hex()),
        ),
    )

    signed_tx = tx.sign(account.key.hex())
    tx_hash = w3.eth.send_raw_transaction(signed_tx.encode())
    ```

    ```python [provider.py]
    from web3 import Web3
    from eth_account import Account

    w3 = Web3(Web3.HTTPProvider("https://rpc.tempo.xyz"))
    account = Account.from_key("0x...")
    ```

    :::
  </Tab>

  <Tab title="Go">
    :::code-group

    ```go [main.go]
    package main

    import (
    	"context"
    	"log"
    	"math/big"

    	"github.com/ethereum/go-ethereum/common"
    	"github.com/tempoxyz/tempo-go/pkg/signer"
    	"github.com/tempoxyz/tempo-go/pkg/transaction"
    )

    func buildTransferWithMemoData(to common.Address, amount *big.Int, memo [32]byte) []byte {
    	data := make([]byte, 100) // 4 + 32 + 32 + 32
    	data[0], data[1], data[2], data[3] = 0x76, 0xa8, 0xee, 0x59
    	copy(data[16:36], to.Bytes())
    	amount.FillBytes(data[36:68])
    	copy(data[68:100], memo[:])
    	return data
    }

    func main() {
    	sgn, _ := signer.NewSigner("0x...")
    	c := newClient()
    	ctx := context.Background()

    	nonce, _ := c.GetTransactionCount(ctx, sgn.Address().Hex())

    	recipient := common.HexToAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb")
    	alphaUSD := common.HexToAddress("0x20c0000000000000000000000000000000000001")

    	var memo [32]byte // [!code hl]
    	copy(memo[:], []byte("INV-12345")) // [!code hl]

    	tx := transaction.NewBuilder(big.NewInt(transaction.ChainIdMainnet)).
    		SetNonce(nonce).
    		SetGas(100_000).
    		SetMaxFeePerGas(big.NewInt(25_000_000_000)).
    		SetMaxPriorityFeePerGas(big.NewInt(1_000_000_000)).
    		AddCall( // [!code hl]
    			alphaUSD, // [!code hl]
    			big.NewInt(0), // [!code hl]
    			buildTransferWithMemoData(recipient, big.NewInt(100_000_000), memo), // [!code hl]
    		). // [!code hl]
    		Build()

    	_ = transaction.SignTransaction(tx, sgn)
    	serialized, _ := transaction.Serialize(tx, nil)
    	txHash, _ := c.SendRawTransaction(ctx, serialized)

    	log.Printf("Transaction hash: %s", txHash)
    }
    ```

    ```go [provider.go]
    // [!include ~/snippets/go-provider.go:setup]
    ```

    :::
  </Tab>

  <Tab title="Cast">
    ```bash
    $ cast send \
      0x20c0000000000000000000000000000000000001 \
      "transferWithMemo(address,uint256,bytes32)" \
      0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb \
      100000000 \
      $(cast --format-bytes32-string "INV-12345") \
      --rpc-url $TEMPO_RPC_URL \
      --private-key $PRIVATE_KEY # [!code hl]
    ```
  </Tab>

  <Tab title="Solidity">
    ```solidity
    import {ITIP20} from "tempo-std/interfaces/ITIP20.sol";

    contract PaymentSender {
        ITIP20 public token;

        function sendPaymentWithMemo(
            address recipient,
            uint256 amount,
            bytes32 invoiceId
        ) external {
            token.transferWithMemo(recipient, amount, invoiceId); // [!code hl]
        }
    }
    ```
  </Tab>
</Tabs>

### Batch payment transactions

Send multiple payments in a single transaction using batch transactions:

<Tabs stateKey="library">
  <Tab title="Viem">
    :::code-group

    ```ts twoslash [example.ts]
    // @noErrors
    import { encodeFunctionData, parseUnits } from 'viem'
    import { Abis } from 'viem/tempo'
    import { client } from './viem.config'

    const tokenABI = Abis.tip20
    const recipient1 = '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb'
    const recipient2 = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'

    const calls = [ // [!code hl]
      { // [!code hl]
        to: '0x20c0000000000000000000000000000000000001', // [!code hl]
        data: encodeFunctionData({ // [!code hl]
          abi: tokenABI, // [!code hl]
          functionName: 'transfer', // [!code hl]
          args: [recipient1, parseUnits('100', 6)], // [!code hl]
        }), // [!code hl]
      }, // [!code hl]
      { // [!code hl]
        to: '0x20c0000000000000000000000000000000000001', // [!code hl]
        data: encodeFunctionData({ // [!code hl]
          abi: tokenABI, // [!code hl]
          functionName: 'transfer', // [!code hl]
          args: [recipient2, parseUnits('50', 6)], // [!code hl]
        }), // [!code hl]
      }, // [!code hl]
    ] // [!code hl]

    const hash = await client.sendTransaction({ calls })
    ```

    ```ts twoslash [viem.config.ts]
    // [!include ~/snippets/viem.config.ts:setup]
    ```

    :::
  </Tab>

  <Tab title="Wagmi">
    ```tsx twoslash
    // @noErrors
    import { useSendTransactionSync } from 'wagmi'
    import { encodeFunctionData, parseUnits } from 'viem'
    import { Abis } from 'viem/tempo'

    function BatchPayment() {
      const { sendTransactionSync, isPending } = useSendTransactionSync()

      const tokenABI = Abis.tip20
      const token = '0x20c0000000000000000000000000000000000001'

      return (
        <button
          onClick={() =>
            sendTransactionSync({
              calls: [ // [!code hl]
                { // [!code hl]
                  to: token, // [!code hl]
                  data: encodeFunctionData({ // [!code hl]
                    abi: tokenABI, // [!code hl]
                    functionName: 'transfer', // [!code hl]
                    args: ['0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb', parseUnits('100', 6)], // [!code hl]
                  }), // [!code hl]
                }, // [!code hl]
                { // [!code hl]
                  to: token, // [!code hl]
                  data: encodeFunctionData({ // [!code hl]
                    abi: tokenABI, // [!code hl]
                    functionName: 'transfer', // [!code hl]
                    args: ['0x70997970C51812dc3A010C7d01b50e0d17dc79C8', parseUnits('50', 6)], // [!code hl]
                  }), // [!code hl]
                }, // [!code hl]
              ], // [!code hl]
            })
          }
          disabled={isPending}
        >
          Send Batch Payment
        </button>
      )
    }
    ```
  </Tab>

  <Tab title="Rust">
    :::code-group

    ```rust [example.rs]
    use alloy::{
        primitives::{address, Address, U256},
        providers::{Provider, ProviderBuilder},
        sol_types::SolCall,
    };
    use tempo_alloy::{
        TempoNetwork, contracts::precompiles::ITIP20, primitives::transaction::Call,
        rpc::TempoTransactionRequest,
    };

    mod provider;

    #[tokio::main]
    async fn main() -> Result<(), Box<dyn std::error::Error>> {
        let provider = provider::get_provider().await?;

        let recipient1 = address!("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb");
        let recipient2 = address!("0x70997970C51812dc3A010C7d01b50e0d17dc79C8");
        let token_address: Address = address!("0x20c0000000000000000000000000000000000001");

        let calls = vec![ // [!code hl]
            Call { // [!code hl]
                to: token_address.into(), // [!code hl]
                input: ITIP20::transferCall { // [!code hl]
                    to: recipient1, // [!code hl]
                    amount: U256::from(100_000_000), // [!code hl]
                } // [!code hl]
                .abi_encode() // [!code hl]
                .into(), // [!code hl]
                value: U256::ZERO, // [!code hl]
            }, // [!code hl]
            Call { // [!code hl]
                to: token_address.into(), // [!code hl]
                input: ITIP20::transferCall { // [!code hl]
                    to: recipient2, // [!code hl]
                    amount: U256::from(50_000_000), // [!code hl]
                } // [!code hl]
                .abi_encode() // [!code hl]
                .into(), // [!code hl]
                value: U256::ZERO, // [!code hl]
            }, // [!code hl]
        ]; // [!code hl]

        let pending = provider
            .send_transaction(TempoTransactionRequest {
                calls,
                ..Default::default()
            })
            .await?;
        let tx_hash = pending.tx_hash();

        println!("Batch transaction sent: {tx_hash:?}");

        Ok(())
    }
    ```

    ```rust [provider.rs]
    // [!include ~/snippets/rust-signer-provider.rs:setup]
    ```

    :::
  </Tab>

  <Tab title="Python">
    :::code-group

    ```python [example.py]
    from eth_abi import encode
    from pytempo import Call, TempoTransaction
    from provider import w3, account

    TRANSFER_SELECTOR = bytes.fromhex("a9059cbb")
    ALPHA_USD = "0x20c0000000000000000000000000000000000001"

    def build_transfer(to: str, amount: int) -> str:
        data = TRANSFER_SELECTOR + encode(
            ["address", "uint256"], [to, amount]
        )
        return "0x" + data.hex()

    tx = TempoTransaction.create(
        chain_id=w3.eth.chain_id,
        gas_limit=200_000,
        max_fee_per_gas=w3.eth.gas_price * 2,
        max_priority_fee_per_gas=w3.eth.gas_price,
        nonce=w3.eth.get_transaction_count(account.address),
        calls=( # [!code hl]
            Call.create( # [!code hl]
                to=ALPHA_USD, # [!code hl]
                data=build_transfer("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb", 100_000_000), # [!code hl]
            ), # [!code hl]
            Call.create( # [!code hl]
                to=ALPHA_USD, # [!code hl]
                data=build_transfer("0x70997970C51812dc3A010C7d01b50e0d17dc79C8", 50_000_000), # [!code hl]
            ), # [!code hl]
        ), # [!code hl]
    )

    signed_tx = tx.sign(account.key.hex())
    tx_hash = w3.eth.send_raw_transaction(signed_tx.encode())
    ```

    ```python [provider.py]
    from web3 import Web3
    from eth_account import Account

    w3 = Web3(Web3.HTTPProvider("https://rpc.tempo.xyz"))
    account = Account.from_key("0x...")
    ```

    :::
  </Tab>

  <Tab title="Go">
    :::code-group

    ```go [main.go]
    package main

    import (
    	"context"
    	"log"
    	"math/big"

    	"github.com/ethereum/go-ethereum/common"
    	"github.com/tempoxyz/tempo-go/pkg/signer"
    	"github.com/tempoxyz/tempo-go/pkg/transaction"
    )

    func buildTransferData(to common.Address, amount *big.Int) []byte {
    	data := make([]byte, 68)
    	data[0], data[1], data[2], data[3] = 0xa9, 0x05, 0x9c, 0xbb
    	copy(data[16:36], to.Bytes())
    	amount.FillBytes(data[36:68])
    	return data
    }

    func main() {
    	sgn, _ := signer.NewSigner("0x...")
    	c := newClient()
    	ctx := context.Background()

    	nonce, _ := c.GetTransactionCount(ctx, sgn.Address().Hex())

    	recipient1 := common.HexToAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb")
    	recipient2 := common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")
    	alphaUSD := common.HexToAddress("0x20c0000000000000000000000000000000000001")

    	tx := transaction.NewBuilder(big.NewInt(transaction.ChainIdMainnet)).
    		SetNonce(nonce).
    		SetGas(200_000).
    		SetMaxFeePerGas(big.NewInt(25_000_000_000)).
    		SetMaxPriorityFeePerGas(big.NewInt(1_000_000_000)).
    		AddCall(alphaUSD, big.NewInt(0), buildTransferData(recipient1, big.NewInt(100_000_000))). // [!code hl]
    		AddCall(alphaUSD, big.NewInt(0), buildTransferData(recipient2, big.NewInt(50_000_000))). // [!code hl]
    		Build()

    	_ = transaction.SignTransaction(tx, sgn)
    	serialized, _ := transaction.Serialize(tx, nil)
    	txHash, _ := c.SendRawTransaction(ctx, serialized)

    	log.Printf("Batch transaction sent: %s", txHash)
    }
    ```

    ```go [provider.go]
    // [!include ~/snippets/go-provider.go:setup]
    ```

    :::
  </Tab>

  <Tab title="Cast">
    ```bash
    $ cast batch-send \
      --rpc-url $TEMPO_RPC_URL \
      --private-key $PRIVATE_KEY \
      --call "0x20c0000000000000000000000000000000000001::transfer(address,uint256):0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb,100000000" \
      --call "0x20c0000000000000000000000000000000000001::transfer(address,uint256):0x70997970C51812dc3A010C7d01b50e0d17dc79C8,50000000" # [!code hl]
    ```
  </Tab>

  <Tab title="Solidity">
    ```solidity
    import {ITIP20} from "tempo-std/interfaces/ITIP20.sol";

    contract BatchPaymentSender {
        ITIP20 public token;

        struct Payment {
            address recipient;
            uint256 amount;
        }

        function batchPay(Payment[] calldata payments) external {
            for (uint256 i = 0; i < payments.length; i++) {
                token.transfer(payments[i].recipient, payments[i].amount); // [!code hl]
            }
        }
    }
    ```
  </Tab>
</Tabs>

### Payment events

When you send a payment, the token contract emits events:

* **Transfer**: Standard ERC-20 transfer event
* **TransferWithMemo**: Additional event with memo (if using `transferWithMemo`)

You can filter these events to track payments in your off-chain systems.

## Best practices

### Loading state

Users should see a loading state when the payment is being processed.

You can use the `isPending` property from the `useTransferSync` hook to show pending state to the user.

### Error handling

If an error unexpectedly occurs, you can display an error message to the user by using the `error` property from the `useTransferSync` hook.

```tsx
import { Hooks } from 'wagmi/tempo'

function SendPayment() {
  const sendPayment = Hooks.token.useTransferSync()

  return (
    <>
      {/* ... your paymentform ... */}
      {sendPayment.error && <div>Error: {sendPayment.error.message}</div>}
    </>
  )
}
```

## Learning resources

<Cards>
  <Card description="Learn more about the TypeScript SDK" to="/docs/sdk/typescript" icon="lucide:code" title="TypeScript SDK" />

  <Card description="Learn more about transactions on Tempo" to="/docs/protocol/transactions" icon="lucide:send" title="Transactions" />
</Cards>
