A working swap integration needs an explicit sequence: obtain a route, resolve its spending permissions, prepare a fresh transaction, simulate that transaction, request wallet confirmation, and track the result. An HTTP success response completes only one part of that sequence. It does not prove that the wallet can spend the input token or that the swap has executed.
Model the integration state
This provider-neutral pseudocode describes UI states. It is not an API endpoint, executable trading code or a complete integration. The user's review must bind the chain, assets, amount, recipient and limits to the transaction being signed.
state = "quoting"
quote = requestFreshQuote(tradeIntent)
validateQuoteAgainstIntent(quote, tradeIntent)
state = "review"
requireExplicitUserConfirmation(quote)
prepareRequiredPermissions(quote)
if quote.isExpired: returnToQuoteReview()
state = "simulating"
simulateAgainstCurrentState(quote.transaction)
state = "awaiting_signature"
hash = requestWalletTransaction(quote.transaction)
state = "pending"
receipt = waitForReceipt(hash)
state = receipt.succeeded ? "confirmed" : "reverted"
verifyRecipientBalances(receipt)| Observed state | UI response |
|---|---|
| User rejects the signature | Return to review. Do not claim a transaction was submitted. |
| Receipt request times out | Keep the hash and show status unknown or pending. Check before offering another submission. |
| Quote expires before signing | Request a fresh quote and show changed terms for another review. |
| Receipt indicates failure | Show reverted, preserve the hash and investigate the cause before retrying. |
This guide describes a same-chain EVM swap funded by the connected account. The examples reference 0x AllowanceHolder where a concrete API is useful. Signed intent orders, cross-chain settlement and account-abstraction batching require additional states; they should not be forced into this transaction flow.
Capture the trade as an immutable request
Before requesting a quote, record the chain ID, input and output asset identities, input amount, sender, recipient and slippage setting. Treat a token as a chain-and-address pair, with a separate type for native currency. Symbols are display labels. A token named USDC on one chain does not identify a contract on another.
Convert the user-entered decimal string into integer base units without passing it through floating-point arithmetic. In a hypothetical six-decimal token, 12.345678 becomes 12345678 base units; 12.3456789 requires rejection or an explicit rounding decision. Do not silently round up the amount being sold. The ERC-20 specification defines the decimals display relationship and makes metadata methods optional, so missing metadata needs a supported fallback or a clear unsupported-token state.
Give this input snapshot a local request ID. If the user changes the amount while a quote is in flight, discard the older response even if it arrives last. Subscribe to the wallet's account and chain change events; either event invalidates account-dependent approval and execution state.
Use a quote response for its documented purpose
A provider may offer an indicative price endpoint, a separate transaction-building endpoint, or both. In the 0x AllowanceHolder flow, the price request helps populate the form, while the firm quote provides transaction data. The v2 request uses chainId, sellToken, buyToken, sellAmount and taker; authenticated calls include the documented API key and version headers.
Keep service credentials in a backend when they are intended to be secret. The browser can send the proposed trade to that backend, which validates the request and calls the approved provider. A quote proxy should accept a narrow trade schema, not an arbitrary upstream URL supplied by the browser.
Check liquidity availability and validate the response shape before reading transaction fields. Then inspect execution issues. For example, 0x reports allowance, balance and simulation issues separately. A usable price alongside insufficient balance is appropriate for browsing, but it is not a reason to enable the final submission button.
Approve the spender selected by the execution path
Read allowance for the actual token, owner and spender. If it is sufficient, avoid an unnecessary approval transaction. If it is insufficient, explain the permission being requested and submit the approval through the wallet. Wait for its successful receipt, then read allowance again before continuing.
The spender is not a universal synonym for the swap destination. 0x distinguishes allowance targets from execution entry points and explicitly warns against approving its Settler contract. Its response identifies the intended allowance target. Resolve and validate that target using the chosen provider's documented deployment rules; do not copy an address from an unrelated integration.
Provider variants also matter. PancakeSwap's Unified Swap examples use different approval paths for an aggregator candidate and a PancakeSwap X candidate. A generic adapter that assumes every winning quote uses yesterday's spender will fail when the engine changes.
Refresh, inspect and simulate the executable transaction
Approval can take long enough for the displayed route to become stale. Request a fresh executable quote after approval, and show the user any material change to the expected output or fee. Bind the result to the current request snapshot. Never combine the destination from one quote with calldata or value from another.
Inspect the execution chain, destination, calldata and native value together. A native-input swap commonly carries value, while a token-input swap may have different funding requirements. Preserve the provider's transaction semantics instead of assuming every swap has zero value. Keep enough native currency for the transaction's network fee.
Use eth_call to preflight the actual sender, destination, data and value against an appropriate recent state. Estimate gas for the same call. A revert during estimation may reflect a missing allowance, expired route or contract restriction; simply raising the gas limit does not repair those causes. A successful simulation remains evidence about a particular state, not a guarantee about the later block in which the transaction lands.
Submit once, then track evidence
Immediately before opening the wallet, verify that the account and chain still match. Submit only the reviewed transaction. If the wallet returns error 4001, record a user rejection and return control to the form; do not automatically reopen the request.
Once a transaction hash is available, store it with the chain and local attempt ID. Display a pending state. The transaction receipt API supplies execution evidence after inclusion. A receipt's status distinguishes execution success from failure. For a successful transaction, reconcile the received token and amount rather than turning the old estimate into an asserted fill.
If broadcasting times out, preserve an unknown-outcome state. Recover the hash or account transaction history before offering a fresh submission: the first transaction may already exist. The same principle applies when the browser closes during a wallet prompt. An interrupted interface must not erase the distinction between rejected, submitted and confirmed.
Evidence to collect before launch
- A quote cannot survive a change of account, chain, token, amount or recipient.
- Insufficient balance and insufficient allowance lead to different recovery actions.
- Approval failure prevents swap submission, and approval success triggers fresh validation.
- Native transaction value survives every serialization boundary.
- A rejected wallet prompt creates no automatic retry loop.
- A timeout preserves the transaction attempt for reconciliation.
- A successful HTTP response or transaction hash never appears as a completed swap.
These are proposed acceptance checks, not claims of testing against live providers. Run them against fixtures and a controlled integration environment, then document which provider paths and wallet types the application actually supports.
Sources & verification (9)
Source-check date is recorded in the article details. URLs are provided for manual verification. Use Copy to keep this page open.
- ERC-20: Token Standard
Token units, optional metadata and allowance semantics
https://eips.ethereum.org/EIPS/eip-20 - EIP-1193: Ethereum Provider JavaScript API
Account/chain events and user rejection error
https://eips.ethereum.org/EIPS/eip-1193 - Get Started with Swap API
AllowanceHolder sequence, request parameters and transaction payload
https://docs.0x.org/docs/introduction/quickstart/swap-tokens-with-0x-swap-api - Issues & Error Codes
Distinct allowance, balance and incomplete simulation issues
https://docs.0x.org/docs/introduction/api-issues - Contracts
Allowance target versus execution entry point; Settler warning
https://docs.0x.org/docs/core-concepts/contracts - TypeScript examples
Different approval and execution paths for agg and pcsx candidates
https://developer.pancakeswap.finance/contracts/unified-swap-api/typescript - eth Namespace
eth_call evaluates transaction context without publishing a transaction
https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-eth - JSON-RPC API
Transaction submission, receipt retrieval and transaction fields
https://ethereum.org/developers/docs/apis/json-rpc/ - EIP-658: Embedding transaction status code in receipts
Receipt success/failure status
https://eips.ethereum.org/EIPS/eip-658