Use this file to discover all available pages before exploring further.
This guide walks through building an automated deposit sweep. Whenever funds land in a deposit wallet, your system detects the deposit and transfers the funds to a central treasury wallet.
Always respond with 200 immediately and process the sweep asynchronously. See webhook best practices.
3
Execute the sweep transfer
From your queue processor, create a transfer from the deposit wallet to your treasury wallet. The transfer kind must match the deposit type.
const TREASURY_WALLET_ID = 'wa-treasury-xxx'// Get the treasury wallet addressconst treasury = await dfns.wallets.getWallet({ walletId: TREASURY_WALLET_ID,})async function executeSweep(deposit: { walletId: string kind: string amount: string contract?: string txHash: string}) { // Map blockchain event kind to transfer kind const transferBody = buildTransferBody(deposit, treasury.address) const transfer = await dfns.wallets.createTransfer({ walletId: deposit.walletId, body: { ...transferBody, externalId: `sweep-${deposit.txHash}`, // idempotency }, }) console.log(`Sweep initiated: ${transfer.id} (${transfer.status})`)}function buildTransferBody( deposit: { kind: string; amount: string; contract?: string }, treasuryAddress: string) { switch (deposit.kind) { case 'NativeTransfer': return { kind: 'Native' as const, to: treasuryAddress, amount: deposit.amount, } case 'Erc20Transfer': return { kind: 'Erc20' as const, to: treasuryAddress, amount: deposit.amount, contract: deposit.contract!, } case 'SplTransfer': return { kind: 'Spl' as const, to: treasuryAddress, amount: deposit.amount, contract: deposit.contract!, } // Handle other transfer kinds as needed default: throw new Error(`Unsupported transfer kind: ${deposit.kind}`) }}
Use externalId with a value derived from the deposit transaction hash. This ensures the same deposit is never swept twice, even if your webhook handler processes the event more than once. See idempotency.
4
Track sweep status
Monitor sweep results via the webhook events you subscribed to in step 1:
switch (event.kind) { case 'wallet.transfer.confirmed': const { transferRequest } = event.data console.log(`Sweep confirmed: ${transferRequest.id}`) console.log(`Tx hash: ${transferRequest.txHash}`) // Mark sweep as complete in your database break case 'wallet.transfer.failed': const { transferRequest: failed } = event.data console.error(`Sweep failed: ${failed.id}, ${failed.reason}`) // Alert and retry break}
The deposit wallet needs native tokens to pay gas fees for the sweep transaction. This creates a chicken-and-egg problem: the wallet receives a token deposit, but may not have native tokens for gas.Two options:
Use a fee sponsor so the deposit wallet doesn’t need to hold native tokens at all. A single funded fee sponsor wallet pays gas for all your sweep transactions.
const transfer = await dfns.wallets.createTransfer({ walletId: deposit.walletId, body: { kind: 'Erc20', to: treasuryAddress, amount: deposit.amount, contract: deposit.contract, feeSponsorId: 'fsp-xxx', // your fee sponsor ID },})
Fund each deposit wallet with a small amount of native tokens when you create it. Top up as needed after sweeps. This works on all networks but requires more operational overhead.
Instead of sweeping immediately after each deposit, you can batch sweeps on a schedule (e.g., every hour or once daily). This is particularly useful for:
UTXO networks (Bitcoin, Litecoin, Dogecoin): batching consolidates many small UTXOs and reduces total fees
High-volume systems: reduces the number of transactions and associated gas costs
To batch, store detected deposits in a queue and process them periodically instead of calling executeSweep inline.
Handle duplicates: Webhooks may be delivered more than once. Use externalId for idempotency and track processed deposits in your database.
Reconciliation: Run periodic reconciliation jobs using wallet history to catch deposits your webhook may have missed.
Policies: Add policies to control sweep behavior (e.g., velocity limits, amount thresholds). Service accounts should never have approval authority. Keep automation and oversight separate.
Tier-1 networks only: Deposit detection via wallet.blockchainevent.detected is only available on Tier-1 networks. For Tier-2 networks, poll wallet history instead.