Skip to navigation

Wallets and gas

This page is for partners whose customers use ordinary wallets. The customer’s registered wallet signs every transaction and pays its network fee (“gas”) in POL. The Buy walkthrough and Sell walkthrough tell you when each transaction is needed.

Partners that use smart accounts execute prepared transactions through their own wallet infrastructure and arrange network fees or sponsorship themselves. Report the transaction hash of the transaction included in a block through the same transaction API. Do not report a UserOperation identifier, and do not send a UserOperation signature in signedTransaction, which accepts only a serialized signed transaction.

1. Take the prepared transaction

Every blockchain step comes back from Sinbad as a prepared transaction:

FieldMeaning
preparationIdThe ID you send back when you submit the signed transaction.
purposeWhat the transaction does. One of approvalReset, approval, createEscrow, markPaid, release, cancelPrePaid, openDispute or mutualResolveDispute.
chainId, from, to, data, valueThe transaction to sign. value is always "0" and from is always the customer’s registered wallet.
expiresAtThe signing deadline. For approval and escrow transactions it is the breakdown’s deadline. For trade actions it is null, so they have no signing deadline.

Prepared transactions come from a breakdown (escrowTransaction), from POST /v1/trade-breakdowns/{breakdownId}/approval (transactions) and from POST /v1/trades/{tradeId}/actions/{action}/execution (transaction). Use to, data and value unchanged.

2. Build and sign the transaction

Sign an EIP-1559 (type 2) transaction. You choose the nonce, gas limit and fees, and the customer’s wallet pays for them.

  • Set chainId, to, data and value from the prepared transaction, and leave the access list empty.
  • Use the wallet’s next nonce, counting pending transactions: call eth_getTransactionCount with the pending tag.
  • Set gasLimit and maxFeePerGas above 0, and keep maxPriorityFeePerGas at or below maxFeePerGas. Estimate the gas for the call and add a margin.

Before it accepts signed bytes, Sinbad checks that the signer is from and that the chain, destination, calldata and value match the prepared transaction. A mismatch returns 400 VALIDATION_ERROR.

3. Submit the signed transaction

Call POST /v1/transactions with the signed bytes in signedTransaction (hex, at most 16,000 characters) and the preparationId. The Idempotency-Key header is required: 8 to 128 characters from A-Z a-z 0-9 _ -.

POST /v1/transactions
Idempotency-Key: tx-<preparationId>
Content-Type: application/json
{ "preparationId": "<preparationId>", "signedTransaction": "0x02..." }

Sinbad broadcasts the transaction and tracks it. The answer is 202, with a transaction object (status, transactionHash, confirmations, requiredConfirmations) and a trade that is null until the trade exists. A 202 means tracking has started, not that the step is done.

To broadcast the transaction yourself, send transactionHash instead of signedTransaction. Send exactly one of the two, and use the hash of the transaction that was included in a block. Two responses are normal when you report a hash:

  • A hash report for an escrow transaction can return 202 with PENDING_CONFIRMATION before a receipt is visible.
  • A hash report for an action that is unavailable or unconfirmed can return 502 UPSTREAM_ERROR. Retry the same report later, and do not broadcast again.

4. Keep POL in the customer’s wallet

The wallet pays gas in POL for every transaction: approvalReset, approval, createEscrow, markPaid, cancelPrePaid, release, openDispute and mutualResolveDispute.

When you submit signed bytes, Sinbad checks the balance against the transaction’s maximum fee (gasLimit times maxFeePerGas), including pending transactions. If the wallet is short, the call fails before anything is broadcast or recorded with 400 INSUFFICIENT_BALANCE (numeric ID 4001) and details.kind: "NATIVE_GAS". The details include:

FieldMeaning
asset"POL"
chainIdThe chain’s integer ID.
requiredBalanceWei, balanceWei, shortfallWeiThe three amounts as integer strings.
requiredBalancePol, balancePol, shortfallPolThe same amounts in POL.

Top up at least the shortfall plus a margin, then submit the same signed bytes again. The prepared transaction stays valid until its expiresAt. Because a breakdown lives for only 300 seconds, fund customer wallets in advance and do not wait for this error.

If a SELL wallet is short of USDT, the error has no details.kind: "NATIVE_GAS", because that is a token shortage. A report that sends only transactionHash is not balance-checked.

Sinbad checks the nonce before the balance. If the wallet’s pending nonce no longer equals the one you signed, the call fails with 409 CONFLICT (“Wallet nonce changed”). Read the nonce again, sign again and submit.

5. Track the status

Poll GET /v1/transactions/{preparationId} and use webhooks for the trade itself. Read requiredConfirmations from the response rather than hard-coding a value. The trade can update after the transaction confirms, so read the trade again instead of submitting again.

transaction.statusMeaning
READY_TO_SIGNPrepared. Nothing has been submitted.
BROADCAST_UNKNOWNSubmitted. Sinbad has not yet seen the network accept it, and keeps checking and re-sending the same signed bytes.
PENDING_CONFIRMATIONThe transaction is on chain and waiting for confirmations.
CONFIRMEDA successful receipt at the required depth.
REVERTEDThe transaction failed on chain.
REJECTEDA check failed before Sinbad broadcast the transaction: a conflict, a disallowed action or no liquidity.
REPLACEDAnother transaction with the same nonce was confirmed instead.
EXPIREDThe signing window ended without a broadcast.
REVIEW_REQUIREDThe evidence does not match, or a broadcast stayed unresolved for more than 24 hours. Contact Sinbad and do not submit again.

6. Use one transaction per prepared transaction

Sinbad normally binds one transaction hash to each prepared transaction.

  • After a definite failure (REVERTED, REJECTED or EXPIRED), request a new prepared transaction only if the action is still needed and permitted. A trade action returns a new prepared transaction on every call. An approval needs a new Idempotency-Key. A failed escrow transaction needs a new breakdown, once the previous breakdown has released the customer’s open trade.
  • If you reported the wrong hash for an externally sent escrow transaction, report the correct hash for the same prepared transaction with a new Idempotency-Key. Sinbad accepts the correction only after a confirmed receipt proves that exact checkout. It cannot replace tracking that is already confirmed.

A pending, missing or uncertain receipt is not permission to send another transaction. The first one may still execute.

7. Retry safely

The Idempotency-Key is bound to the preparationId and to a hash of the signed bytes.

  • Sending the same bytes with the same key is a safe replay. It returns the current state and never creates a second transaction.
  • Sending different bytes with the same key returns 409 CONFLICT.
  • After Sinbad has accepted a transaction for a prepared transaction, different bytes for it are refused with 409 CONFLICT, even with a new key. Re-sign with different fees only while nothing has been accepted.
  • A call that failed before acceptance, such as one with NATIVE_GAS, recorded nothing. Fix the cause and submit again. Reuse the key only with identical signed bytes. If you sign different fields, use a new key.
  • Sinbad keeps submission-key bindings for 30 days.

The verified escrow hash correction in step 6 is the one exception that needs a fresh key. It never broadcasts another transaction.

8. When you do not know whether a submission reached the chain

If your submit call times out, or the status is BROADCAST_UNKNOWN, assume the transaction may still reach the chain.

  1. Do not build a second transaction for the same step, and do not create a new breakdown or action.
  2. Either send the identical request again (same key, same preparationId, same signed bytes), or call GET /v1/transactions/{preparationId}. Sinbad keeps tracking and re-sends the same signed bytes if needed.
  3. Wait for PENDING_CONFIRMATION, CONFIRMED, REVERTED, REPLACED, EXPIRED or REJECTED.
  4. If the result is EXPIRED, REJECTED or REVERTED, prepare the step again as described in step 6.

Sinbad simulates the call before it broadcasts signed bytes. If the simulation fails, the response is a generic 503 UPSTREAM_ERROR. Check the allowance, the balance and the trade state before you replay the identical bytes. Temporary RPC problems also return 502 or 503: keep the request and retry with backoff. Unresolved external escrow tracking reaches REVIEW_REQUIRED after 24 hours without expiring the breakdown or releasing the customer’s open trade.