implementation
Fireblocks whitelisted wallets
Fireblocks whitelisted wallets
Objective
DAX outbound transfers to saved payees and bulk-payout recipients use Fireblocks external wallets instead of one-time addresses. OTA remains available to explicit developer tooling, but customer-facing sends never silently fall back to it.
Delivered architecture
payee_walletsowns the immutable destination lifecycle independently from contact details inpayees.- Wallets start as
draft, move through Fireblocks Admin Quorum approval aspending, and become sendable only whenactive. - Live and mock Fireblocks providers implement create, add-asset, read, and remove-asset operations for external wallets.
- Transfer requests use an explicit destination union:
EXTERNAL_WALLETorONE_TIME_ADDRESS. - Normal sends resolve the selected active payee wallet again immediately before broadcast and fail if the address or wallet identity changed.
- Bulk-payout validation resolves every ready address to an active whitelist entry and snapshots its Fireblocks identifiers. Unmatched rows are blocked.
- Fireblocks whitelist webhooks synchronize added/removed assets through the durable provider-webhook inbox.
Rollout
- Deploy migration
0042_payee_wallet_whitelist.sqlbefore the application. Existing active payee addresses are copied asdraft; none are trusted or submitted to Fireblocks automatically. - Ensure the application API user has Fireblocks permissions to create external wallets and add assets. Configure the Admin Quorum for whitelist approvals.
- Review the Fireblocks Transaction Authorization Policy so transfers from the parent vault to external wallets are allowed for the application initiator.
- In DAX, open each payee and select request whitelist. Approve the resulting configuration change in Fireblocks, then use sync status if the webhook has not yet arrived.
- Confirm the wallet shows
activebefore enabling outbound processing. - Verify one transfer per supported network family before enabling bulk payouts: BTC, EVM, Litecoin, and Tron.
Operational invariants
- A provider-approved address is never edited in place. Editing the payee address archives the previous local wallet and creates a new draft.
pending,rejected,removed, anderrorwallets cannot be selected for a normal send.- A send snapshots the local and Fireblocks wallet IDs. The worker re-resolves the active wallet and rejects a changed destination before calling Fireblocks.
- Bulk payout rows without an active external-wallet snapshot never broadcast.
- Fireblocks status remains authoritative; DAX cannot manually force a wallet to
active. - Idempotency keys are deterministic and capped at Fireblocks' 40-character limit.
Verification
pnpm typechecknode node_modules/vitest/vitest.mjs run- Production
next build - Provider helper tests cover OTA and external-wallet request shapes.
- Payout-engine tests cover active whitelist resolution.
Follow-up operations
- Add an admin-wide whitelist queue if operations need to manage approvals across merchants without using the Fireblocks console.
- Add a scheduled reconciliation job for wallets that remain pending beyond the expected Admin Quorum SLA; webhook delivery and manual sync already cover the correctness path.
- Introduce an explicitly approved OTA product flow only if the business requires arbitrary one-off withdrawals.