For developers
Batching & transactions
A confidential distribution is several onchain actions. Obscura runs them as one all-or-nothing signature using EIP-5792 and EIP-7702, and falls back to separate transactions when a wallet cannot batch.
Why batching matters
A confidential disperse is not a single call. Before tokens move, the sender may need to register disperse sub-wallets, approve the disperse contract as an ERC-7984 operator, and only then run the payout. Done the old way that is three wallet prompts, in order, with a real risk of stopping half way and leaving a confusing partial setup.
Batching turns that into one signature that either fully succeeds or fully reverts. Two EIPs make it possible.
EIP-5792: Wallet Call API
EIP-5792 standardizes how a dapp hands a wallet a batch of calls and tracks them. It defines four JSON-RPC methods:
| Method | Purpose |
|---|---|
wallet_getCapabilities | Feature detection: does this account and chain support atomic batching? |
wallet_sendCalls | Submit an array of calls (to, data, value) as one bundle. |
wallet_getCallsStatus | Poll a bundle by its id for status and receipts. |
wallet_showCallsStatus | Ask the wallet to show its own status UI for a bundle. |
The key signal is the atomic capability, which has three states:
- supported - the wallet executes the calls atomically and contiguously.
- ready - the wallet can upgrade to atomic execution, pending user approval.
- unsupported - no atomicity guarantee; fall back to separate transactions.
A dapp sets atomicRequired when it needs all-or-nothing execution rather than best-effort. Obscura reaches these through wagmi's useCapabilities and useSendCalls.
EIP-7702: smart EOAs
EIP-5792 defines the interface, but a plain externally owned account (EOA) historically could not batch. EIP-7702, shipped in the Pectra upgrade (May 2025), closes that gap. It adds a new transaction type (0x04, "set code") that lets an EOA adopt smart contract code by writing a delegation designator (0xef0100 || address) into its own account, authorized by a signed tuple [chain_id, address, nonce, y_parity, r, s].
Its motivation names three UX wins directly:
- Batching - many operations in one atomic transaction.
- Sponsorship - one account pays gas for another.
- Privilege de-escalation - scoped session keys weaker than the main key.
In practice, a 7702-upgraded EOA is what makes an ordinary wallet report atomic: supported or ready. Smart-contract accounts (ERC-4337) already batch, so both paths converge on the same one-signature experience.
How Obscura batches
Obscura builds the whole disperse as one call list, checks capabilities, then picks a path.
// Detect atomic-batch support for this account + chain (EIP-5792)const { data: caps } = useCapabilities({ account, chainId });const canBatch = caps?.atomic?.status === "supported" || caps?.atomic?.status === "ready";// Build every step of the disperse as one list of callsconst calls = [ // register / approve your disperse sub-wallets (once per token) // setOperator(disperseSingleton, FAR_FUTURE_DEADLINE) // disperseConfidentialTokens(token, recipients, handles, subtotals, proof)]; if (canBatch) { // One signature, all-or-nothing (EIP-5792 wallet_sendCalls) const { id } = await sendCallsAsync({ calls, forceAtomic: true }); await waitForCallsStatus(config, { id });} else { // Fallback: one transaction per call, in order for (const call of calls) { const hash = await sendTransactionAsync(call); await waitForTransactionReceipt({ hash }); }}Assemble the calls
register or approveUserWalletsForToken), the operator approval (setOperator), and the disperse call, only including the steps that are still needed.Check atomic support
useCapabilities reports the atomic status for the connected account on Sepolia.Send atomically
sendCalls submits the bundle with forceAtomic, so setup and payout are all-or-nothing and can never half-apply.One signature, all-or-nothing
With a batching wallet, a full disperse (setup, approval, and payout) is a single prompt that either lands completely or reverts completely. No stranded approvals, no partial setup.The sequential fallback
If the wallet cannot batch (atomic: unsupported, and not a smart account), Obscura runs the exact same call list as separate transactions, in order, waiting for each receipt before the next. The end state is identical; the difference is one prompt per step instead of one for the whole flow.
The same fallback also runs when a wallet that reported atomic: supported or ready still rejects wallet_sendCalls at submit time. Obscura treats that failure the same as an unsupported capability and retries the exact same calls sequentially, rather than surfacing the error.
Which wallets can batch?
- 7702-upgraded EOAs (a normal wallet that has opted into smart features).
- Smart-contract accounts (ERC-4337).
- Everything else takes the sequential path automatically; no configuration needed.
Best-UX checklist
- Always feature-detect with
wallet_getCapabilities; never assume batching. - Set
atomicRequiredwhen a partial result would be worse than a full revert. - Keep a sequential fallback so non-batching wallets still work.
- Only include the steps that are still needed, so a second run does not re-submit a done approval.
- Track the bundle with
wallet_getCallsStatusbefore treating it as complete.
References: EIP-5792, EIP-7702, and the EIP-5792 developer reference.