Skip to main content
A Tempo receive policy controls which stablecoins a wallet accepts and who can pay it. Tempo doesn’t revert a payment that breaks the policy. The transfer succeeds, but Tempo holds the funds in the ReceivePolicyGuard contract instead of crediting the wallet. Only the wallet’s recovery authority can release or return them. Apps use receive policies to keep address poisoning, sanction dusting, and spam tokens out of user wallets.

How receive policies work

Each wallet’s receive policy has three parts. Lists live in Tempo’s TIP-403 registry, and each list has one admin. Many wallets can share a list, so one update applies to every wallet that uses it.

Choose a setup

Attackers create new addresses for free, so only an allowlist stops address poisoning.

Prerequisites

  • An operations wallet: a Privy wallet your app controls. It administers the shared lists and acts as recovery authority.
  • Gas sponsorship turned on. To pay fees from the wallet instead, drop sponsor and set fee_token.
  • viem 2.57 or later, which exports the TIP-403 helpers from viem/tempo.
Each step builds a call with a viem/tempo helper and sends it from a Privy wallet as a Tempo transaction (type 118). The examples use Tempo mainnet (eip155:4217). For Tempo Moderato (testnet), use eip155:42431.

1. Create the shared lists

Create each list once, from the operations wallet. The operations wallet becomes the list admin, so only it can edit the list. Start with the token list: the stablecoins the wallets accept. viem names an allowlist 'whitelist' and a denylist 'blacklist', after the TIP-403 policy types.
Then create the sender list the same way, with the addresses allowed to pay user wallets. Read senderPolicyId from its PolicyCreated event.
To accept any sender, skip the sender list and use policy 1.

2. Set the policy on each wallet

Send one transaction from each user wallet, ideally right after creating it. This example points the wallet at both shared lists and makes the operations wallet its recovery authority.
If a user owns the wallet, pass their JWT in authorization_context, as for any other request from that wallet. If the wallet’s Privy policies restrict which contracts the wallet can call, allow calls to the TIP-403 registry (Addresses.tip403Registry in viem/tempo).
Virtual addresses use their master address’s receive policy and can’t set their own.

3. Update the sender list

Add or remove a sender from the operations wallet. Every wallet that uses the list picks up the change at once.
Send the call from opsWalletId as in step 1. For a denylist, use Actions.policy.modifyBlacklist.call with restricted instead of allowed.

4. Review blocked payments

Each blocked payment emits TransferBlocked from ReceivePolicyGuard. Blocked payments can’t be listed onchain, so your app must index these events. The claimReceipt bytes identify the payment.
To release a payment, claim it from the operations wallet. Set to to the user’s wallet to credit it, or to another address to return it.
Send the call from opsWalletId as in step 1. Leave sanction dust held for compliance review.

Things to know

A blocked payment still emits a Transfer event, with ReceivePolicyGuard as the recipient. Credit a deposit only when the confirmed Transfer event names the wallet itself.
  • Test on Moderato first. Actions.receivePolicy.validate(publicClient, {token, sender, receiver}) shows whether the policy accepts a payment, and why not.
  • A claim releases the full amount. To check a payment’s amount first, call Actions.receivePolicy.getBlockedBalance(publicClient, {receipt}).
  • A claim to the receiving wallet skips the receive policy. Only the token’s own policy applies, so claim only approved payments.
  • A new recovery authority applies only to later payments. Payments already blocked keep their original authority.

Using Tempo with Privy

Create wallets and send transactions on Tempo

Tempo policy examples

Control fee tokens, sponsorship, and validity windows with Privy policies

Tempo receive policies

Tempo’s reference for receive policies and the TIP-403 registry

Balance webhooks

Subscribe to deposit and withdrawal events