Skip to content

Send a Payment

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

Demo

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

Send a Payment

demo
1
Create an account, or use an existing one.
2
Add testnet funds to your account.
3
Send 100 AlphaUSD to a recipient.
pnpx gitpick tempoxyz/tempo-ts/tree/main/examples/payments

Steps

Set up Wagmi & integrate accounts

Ensure that you have set up your project with Wagmi and integrated accounts by following either of the guides:

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.

Add Funds

1
Add testnet funds to your account.
Balances
No account detected
AddFunds.ts
import { Hooks } from 'tempo.ts/wagmi'
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>
  )
}

Add send payment logic

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

Send Payment

1
Add testnet funds to your account.
2
Send 100 AlphaUSD to a recipient.
Balances
No account detected
SendPaymentWithMemo.tsx
import { Hooks } from 'tempo.ts/wagmi'
import { parseUnits, stringToHex, pad } from 'viem'
 
function SendPaymentWithMemo() {
  const sendPayment = Hooks.token.useTransferSync() 
 
  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({ 
          amount: parseUnits('100', 6), 
          to: recipient, 
          token: '0x20c0000000000000000000000000000000000001', 
          memo: memo ? pad(stringToHex(memo), { size: 32 }) : undefined, 
        }) 
      }
    }>
      <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
        type="submit"
        disabled={sendPayment.isPending}
      > 
        Send Payment 
      </button> 
    </form>
  )
}

Display receipt

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

SendPaymentWithMemo.tsx
import { Hooks } from 'tempo.ts/wagmi'
import { parseUnits, stringToHex, pad } from 'viem'
 
function SendPaymentWithMemo() {
  const sendPayment = Hooks.token.useTransferSync()
 
  return (
    <>
      {/* ... your payment form ... */}
      {sendPayment.data && ( 
        <a href={`https://explore.tempo.xyz/tx/${sendPayment.data.receipt.transactionHash}`}> 
          View receipt 
        </a> 
      )}
    </>
  )
}

Next steps

Now that you have made a payment you can

Recipes

Basic transfer

Send a payment using the standard transfer function:

example.ts
import { parseUnits } from 'viem'
import { client } from './viem.config'
 
const { receipt } = await client.token.transferSync({
  amount: parseUnits('100', 6), // 100 tokens (6 decimals)
  to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb',
  token: '0x20c0000000000000000000000000000000000001', // AlphaUSD
})

Transfer with memo

Include a memo for payment reconciliation and tracking:

example.ts
import { parseUnits } from 'viem'
import { client } from './viem.config'
 
const invoiceId = pad(stringToHex('INV-12345'), { size: 32 })
 
const { receipt } = await client.token.transferSync({
  amount: parseUnits('100', 6),
  to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb',
  token: '0x20c0000000000000000000000000000000000001',
  memo: invoiceId,
})

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

Using Solidity

If you're building a smart contract that sends payments:

interface ITIP20 {
    function transfer(address to, uint256 amount) external returns (bool);
    function transferWithMemo(address to, uint256 amount, bytes32 memo) external;
}
 
contract PaymentSender {
    ITIP20 public token;
    
    function sendPayment(address recipient, uint256 amount) external {
        token.transfer(recipient, amount);
    }
    
    function sendPaymentWithMemo(
        address recipient, 
        uint256 amount, 
        bytes32 invoiceId
    ) external {
        token.transferWithMemo(recipient, amount, invoiceId);
    }
}

Batch payment transactions

Send multiple payments in a single transaction using batch transactions:

example.ts
import { encodeFunctionData, parseUnits } from 'viem'
import { Abis } from 'tempo.ts/viem'
import { client } from './viem.config'
 
const tokenABI = Abis.tip20
const recipient1 = '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb'
const recipient2 = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'
 
const calls = [
  {
    to: '0x20c0000000000000000000000000000000000001', // AlphaUSD address
    data: encodeFunctionData({
      abi: tokenABI,
      functionName: 'transfer',
      args: [recipient1, parseUnits('100', 6)],
    }),
  },
  {
    to: '0x20c0000000000000000000000000000000000001',
    data: encodeFunctionData({
      abi: tokenABI,
      functionName: 'transfer',
      args: [recipient2, parseUnits('50', 6)],
    }),
  },
]
 
const hash = await client.sendTransaction({ calls })

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.

import { Hooks } from 'tempo.ts/wagmi'
 
function SendPayment() {
  const sendPayment = Hooks.token.useTransferSync()
 
  return (
    <>
      {/* ... your paymentform ... */}
      {sendPayment.error && <div>Error: {sendPayment.error.message}</div>}
    </>
  )
}

Learning resources