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
sponsorand setfee_token. viem2.57 or later, which exports the TIP-403 helpers fromviem/tempo.
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.
senderPolicyId from its PolicyCreated event.
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.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.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 emitsTransferBlocked from ReceivePolicyGuard. Blocked payments can’t be listed onchain, so your app must index these events. The claimReceipt bytes identify the payment.
to to the user’s wallet to credit it, or to another address to return it.
opsWalletId as in step 1. Leave sanction dust held for compliance review.
Things to know
- 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.
Related resources
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

