Skip to Content
Execution rules

Execution rules

Rules the integrator’s frontend and backend must follow around the four API calls. Working code for every rule is in Reference code.

Wallet requirements

  • v1 requires a 65-byte EOA or EIP-7702 authorization signature. ERC-1271 and ERC-4337 contract-wallet signatures are rejected (400 contract_wallet_unsupported).
  • Do not classify every non-empty eth_getCode result as an unsupported contract wallet: EIP-7702 accounts expose delegation code and are supported. Sweep’s server-side validation is authoritative.
  • Keep the raw EIP-1193 provider available. wallet_getCapabilities, wallet_sendCalls and wallet_getCallsStatus are wallet RPC methods; a library signer alone is not enough.
  • Discover injected providers with EIP-6963 rather than assuming a single window.ethereum.
  • Subscribe to accountsChanged (invalidate the current intent), chainChanged (update UI only) and disconnect (stop execution, preserve already-broadcast evidence).
  • Read wallet_getCapabilities immediately before prepare and pass the result unchanged. On failure pass {}; never infer atomic support from the connector name.

Source amounts

  • Token identity is the canonical pair (chainId, address), never symbol. Use the zero address for a native asset.
  • Convert display amounts to raw units without floating-point arithmetic.
  • Never send the full native balance as a source: the source must not spend the gas needed to execute itself.
  • Re-read selected balances immediately before creating the quote.

Executing chain actions

Each chainAction belongs to one source chain. Process actions in returned order. Before every action:

  1. confirm the connected account still equals the prepared wallet;
  2. switch the wallet to chainAction.chainId (wallet_switchEthereumChain; on error 4902, wallet_addEthereumChain with trusted metadata);
  3. verify the resulting active chain;
  4. verify sufficient native gas;
  5. preserve the exact prepared call order.

wallet_sendCalls: submit calls[] through the wallet’s EIP-5792 method and report the returned bundle id immediately — do not wait for receipt hashes.

sendTransaction: submit every entry in transactions[] in order. Approval transactions are part of the immutable plan. Wait for an earlier transaction when the next depends on it. Collect hashes in order and preserve already-broadcast successes if a later transaction fails.

Never:

  • change to, data or value;
  • skip an approval because local allowance state appears different;
  • merge actions from different chains;
  • fabricate call-to-hash mappings;
  • silently retry a user rejection.

Persist before side effects

Persist state before:

  • sending every mutating Sweep request (operation, idempotencyKey, exact body);
  • starting wallet execution;
  • reporting a bundle id or transaction hash;
  • acknowledging a webhook.

A wallet broadcast can succeed while the browser loses connectivity or the backend response is lost. Design recovery around already-broadcast value, not around page state.

Minimal durable attempt record:

type SweepAttemptRecord = { walletAddress: string; recipient: string; intentSessionId: string; attemptId?: string; quoteExpiresAt: string; signableExpiresAt?: string; evidenceDeadlineAt?: string; authorizationSignature?: string; chainResults: Array<{ chainId: number; bundleId?: string; txHashes: string[]; }>; pendingRequests: Array<{ operation: 'quote' | 'prepare' | 'submitted'; idempotencyKey: string; exactBody: string; acknowledged: boolean; }>; };

Recovery

SituationRequired behavior
Request timed out before a responseRetry the same body with the same idempotency key
Quote expired before prepareCreate a new quote and key
Prepare signable window expired before broadcastStart again from a new quote
Wallet broadcast succeeded but reporting failedPreserve evidence and retry /submitted
New receipt hashes appearedSend additive evidence with a new key
User rejected a wallet promptStop that action; preserve earlier chain successes
One source chain failed after another succeededNever discard the successful chain evidence
Connected account changedStop; never use a new account to complete another recipient’s attempt

On application startup:

  1. restore pending attempt records;
  2. retry unacknowledged backend requests with their stored bytes and keys;
  3. resume wallet_getCallsStatus for known bundle ids;
  4. query GET /status and treat execution as already done unless the status proves otherwise;
  5. continue polling non-terminal attempts.

Time windows

FieldMeaning
Quote expiresAtLatest time to call prepare for that quote session
Prepare expiresAtShort attempt freshness window; start wallet signing before it passes
walletAuthorization.message.expiresAtValidity of the EIP-712 authorization, Unix seconds, much longer default
evidenceDeadlineAtLatest time for initial evidence when none has been accepted; additive evidence after acceptance is unbounded by it

Observability

Log identifiers, not secrets or signed authorization payloads:

partnerId, intentSessionId, attemptId, idempotencyKey, walletAddress, chainId, submitMethod, bundleId, txHash, status transition, failedSources reasonCode

Track latency separately for quote, prepare, wallet confirmation, evidence acceptance and settlement. A slow wallet prompt is not an API latency problem.

Last updated on