Deposit swaps

Let customers fund their balance with any token, on any chain.

Ordinary deposits require your customer to send an asset Cheddar custodies. Deposit swaps remove that limit: your customer can send almost any token on almost any chain, and Cheddar credits their balance in USDC.

You request a swap, Cheddar returns a deposit address. Your customer sends the origin token there. It is swapped and credited, and your existing CUSTOMER_DEPOSIT webhook fires exactly as it does for a normal deposit.

Deposit swaps must be enabled for your organization. Until then every call returns 403. Contact the Cheddar team to switch it on.

This is not the Swap feature. That converts an asset you hold and pays out to a whitelisted address. Deposit swaps fund a customer balance from outside.

How it works

StepCall
1List what your customer can sendGET /swap/tokens
2Create the swapPOST /swapdepositAddress + deadline
3Your customer sends the token(your UI)
4Cheddar credits the balanceyour deposit webhook, plus GET /swap/{intentId}

List accepted assets

curl https://api.cheddar.biz/v1/swap/tokens \
-H "x-api-key: YOUR_API_KEY"
{
"tokens": [
{ "assetId": "nep141:eth.omft.near", "blockchain": "eth", "symbol": "ETH", "decimals": 18, "contractAddress": null }
]
}

Always pass assetId back verbatim. Never build one yourself — the format belongs to the upstream provider and can change.

Create a swap

curl -X POST https://api.cheddar.biz/v1/swap \
-H "x-api-key: YOUR_API_KEY" \
-H "content-type: application/json" \
-d '{
"originAssetId": "nep141:eth.omft.near",
"amount": "5000000000000000",
"customerId": "your-customer-id",
"refundTo": "0x7725124cae885D822320Fc5fd8CD83635650D5fe",
"idempotencyKey": "unique-per-swap"
}'

amount is atomic units — 0.005 ETH is 0.005 × 10^18. Decimals like "0.005" are rejected.

{
"intentId": "b9b3c845-045f-4974-a410-df79bc30528a",
"depositAddress": "0x62d4bb081Cff68aba63da5Be434C2794BFBE2418",
"depositMemo": null,
"amountIn": "5000000000000000",
"minAmountIn": "4950000000000000",
"minAmountOut": "9464909",
"deadline": "2026-08-10T20:00:00.000Z",
"status": "PENDING_DEPOSIT",
"existing": false
}

What to show your customer

The deposit address — where they send. Single use; do not reuse it across swaps.

minAmountIn, not amountIn — this is the real floor. Anything at or above it swaps; below it is refunded. Your slippage tolerance is applied to the input side precisely so a customer sending from their own wallet does not fail for being a fraction short.

The deadline — the deposit must arrive before this. Measured in days, not minutes, but read it from the response rather than assuming.

depositMemo, if it is not null, is REQUIRED. Some chains need a memo alongside the address, and omitting it loses the funds permanently. Display it as prominently as the address itself.

Refund address

refundTo must be valid on the origin chain. Cheddar validates the format before anything is spent, so a malformed address fails immediately.

Cheddar cannot verify your customer actually controls that address — format validity is not ownership. Prefer an address from a connected wallet over free text. A valid address they do not own is an unrecoverable loss.

Idempotency

Use one idempotencyKey per swap you actually intend, not per HTTP attempt. Retrying with the same key returns the same swap with existing: true and spends no new quote.

Requesting a second swap for the same customer and origin asset also returns the existing one rather than issuing a new quote. If it is close to expiry it is replaced automatically, so you never receive an address about to stop working.

Track the outcome

curl https://api.cheddar.biz/v1/swap/b9b3c845-045f-4974-a410-df79bc30528a \
-H "x-api-key: YOUR_API_KEY"
StatusTerminalMeaning
PENDING_DEPOSITNoWaiting for your customer to send
PROCESSINGNoDeposit seen, swap running
SUCCESSYesCredited — your deposit webhook fires
INCOMPLETE_DEPOSITYesDeposit arrived below the minimum
REFUNDEDYesReturned to refundTo
FAILEDYesSee failureReason
EXPIREDYesNo deposit arrived in time; nothing moved

Success is pushed, failure is pulled. A successful swap fires your existing CUSTOMER_DEPOSIT webhook. A failed, refunded, or expired one fires nothing. If you rely on webhooks alone, a refund is invisible to you and your customer is left guessing — poll this endpoint until the status is terminal.

Poll every 30–60 seconds while non-terminal. Terminal statuses never change.

Amounts, slippage and fees

Three different “amount out” appear, and only one is a promise:

FieldTrust it?
quotedAmountOutNo — an estimate at quote time
minAmountOutYes — the guaranteed floor
actualAmountOutYes, once the swap has run

Show minAmountOut to customers. Promising the quote and delivering less is a support ticket you created for yourself. Realized slippage is (quotedAmountOut - actualAmountOut) / quotedAmountOut — typically a fraction of the tolerance you requested.

Two fees are reported, and both are easy to misread:

  • withdrawFee — in the destination asset, and already deducted inside the quote. Do not subtract it a second time.
  • refundFee — in the origin asset, charged only on a refund.

They are denominated in different assets, so never add them together.

Swaps below $10 of input are rejected: cross-chain costs would exceed what the customer receives.

USD figures in responses are display only. Never use them for accounting.