Skip to content

POST /swap

An unsigned swap for your wallet: the best route, simulated with its minimum, plus any approvals to send first.

POST https://uprally.co/api/v1/swap
FieldTypeRequiredMeaning
tokenstringyesThe coin
sidebuy · sellyes
amountstringyesWhole units: ETH to spend (buy) or coins to sell (sell)
walletstringyesA 0x address, lower case
slippageBpsintegerDefault 100 (1%)
feeBpsintegerYour own fee on the trade, in ETH, up to 300 (3%)
feeRecipientstringWho gets feeBps
maxPriceImpactPctnumberRefuse past this price impact; default 50
FieldTypeRequiredMeaning
chainId4663yes
tokenstringyesA 0x address, lower case
symbolstring or nullyes
sidebuy · sellyes
amountInobjectyes
amountIn.amountstringyesWhole units, as a decimal string: “0.05”
amountIn.weistringyesThe smallest unit, an integer as a string
amountIn.symbolstring or nullyesETH, or the coin’s ticker
amountOutobjectyesWhat the trader ends up with, after every fee and tax, simulated
amountOut.amountstringyesWhole units, as a decimal string: “0.05”
amountOut.weistringyesThe smallest unit, an integer as a string
amountOut.symbolstring or nullyesETH, or the coin’s ticker
minOutobjectyesThe least the transaction accepts
minOut.amountstringyesWhole units, as a decimal string: “0.05”
minOut.weistringyesThe smallest unit, an integer as a string
minOut.symbolstring or nullyesETH, or the coin’s ticker
slippageBpsintegeryes
priceImpactPctnumber or nullyesPercent (5 means 5%); null when not known
venueobject or objectyes
venue.kindcurveyes
venue.launchpadpons · flapyes
venue.contractstringyesA 0x address, lower case
routesComparedintegeryes
feesobjectyes
fees.takenbooleanyes
fees.listobject[]yes
fees.list[].whouprally · builderyes
fees.list[].recipientstringyesA 0x address, lower case
fees.list[].bpsintegeryes
fees.whystring
sellBackobjectA buy: selling what it bought straight back, simulated
sellBack.oktrueyes
sellBack.sharePctnumberyes
sellBack.ethobjectyes
sellBack.eth.amountstringyesWhole units, as a decimal string: “0.05”
sellBack.eth.weistringyesThe smallest unit, an integer as a string
simulatedFromwallet · test wallet · holderyes
gasUsedstringyesThe smallest unit, an integer as a string
asOfstringyes
walletstringyesA 0x address, lower case
requiresobject[]yesApprovals to send first, in order; empty when the wallet is ready
requires[].stepstringyes
requires[].explainstringyes
requires[].txobjectyesAn unsigned transaction: sign it with the wallet and send it.
requires[].tx.chainId4663yesRobinhood Chain
requires[].tx.fromstringThe wallet that must send it
requires[].tx.tostringyesA 0x address, lower case
requires[].tx.datastringyesThe call data: send it exactly as it is
requires[].tx.valuestringyesETH to send with it, in wei
requires[].tx.gasstringA gas limit with room; absent while a step before it is still needed
txobjectyesAn unsigned transaction: sign it with the wallet and send it.
tx.chainId4663yesRobinhood Chain
tx.fromstringThe wallet that must send it
tx.tostringyesA 0x address, lower case
tx.datastringyesThe call data: send it exactly as it is
tx.valuestringyesETH to send with it, in wei
tx.gasstringA gas limit with room; absent while a step before it is still needed
expiresAtstring or nullyesA router swap reverts after this
checksobjectyes
checks.minimumInCalldatatrueyes
checks.simulatedtrueyes

Captured from the live API on Robinhood Chain, exactly as it was sent (long calldata shortened).

{
"chainId": 4663,
"token": "0x94641b97010608c3827fb058074889f19868ff33",
"symbol": "VRAX",
"side": "sell",
"amountIn": {
"amount": "1.0",
"wei": "1000000000000000000",
"symbol": "VRAX"
},
"amountOut": {
"amount": "0.000002679135485646",
"wei": "2679135485646",
"symbol": "ETH"
},
"minOut": {
"amount": "0.000002652344130789",
"wei": "2652344130789",
"symbol": "ETH"
},
"slippageBps": 100,
"priceImpactPct": 0,
"venue": {
"kind": "pool",
"hops": [
{
"dex": "uniswap",
"version": "v3",
"tokenIn": "0x0000000000000000000000000000000000000000",
"tokenOut": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
"pool": null,
"fee": 100
},
{
"dex": "uniswap",
"version": "v4",
"tokenIn": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
"tokenOut": "0x94641b97010608c3827fb058074889f19868ff33",
"pool": "0x338fce553e60c8b0acccde1956bc378586ef6f2935339ebae5804d429d461e44",
"fee": 0,
"tickSpacing": 200,
"hooks": "0xe5e702641ea86f4ae6cc3cdaed2b886f976be044"
}
]
},
"routesCompared": 6,
"fees": {
"taken": true,
"list": [
{
"who": "builder",
"recipient": "0x8888888888888888888888888888888888888888",
"bps": 25
}
]
},
"simulatedFrom": "wallet",
"gasUsed": "292136",
"asOf": "2026-10-16T03:36:40.724Z",
"wallet": "0xccd858410c1ddf1b9b0c8293d38c0ed55349a15d",
"requires": [
{
"step": "approve-permit2",
"explain": "Let Permit2 move this coin for you (once per coin): Uniswap's router takes coins only through it.",
"tx": {
"chainId": 4663,
"from": "0xccd858410c1ddf1b9b0c8293d38c0ed55349a15d",
"to": "0x94641b97010608c3827fb058074889f19868ff33",
"data": "0x095ea7b3000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"value": "0"
}
},
{
"step": "permit2-allow-router",
"explain": "Let Uniswap's router take this coin through Permit2 for 30 days.",
"tx": {
"chainId": 4663,
"from": "0xccd858410c1ddf1b9b0c8293d38c0ed55349a15d",
"to": "0x000000000022d473030f116ddee9f6b43ac78ba3",
"data": "0x87517c4500000000000000000000000094641b97010608c3827fb058074889f19868ff33…",
"value": "0"
}
}
],
"tx": {
"chainId": 4663,
"from": "0xccd858410c1ddf1b9b0c8293d38c0ed55349a15d",
"to": "0x204faca1764b154221e35c0d20abb3c525710498",
"data": "0x3593564c0000000000000000000000000000000000000000000000000000000000000060…",
"value": "0",
"gas": "488204"
},
"expiresAt": "2026-10-16T03:46:39.000Z",
"checks": {
"minimumInCalldata": true,
"simulated": true
}
}

Every error is { "error": { "code", "message", "fix"? } }. This endpoint can answer:

  • INVALID_ADDRESS: A field that must be a 0x address is not one; field says which.
  • INVALID_SIDE: side is “buy” (ETH for the coin) or “sell” (the coin for ETH).
  • INVALID_AMOUNT: The amount is not a positive decimal in whole units.
  • TOO_MANY_DECIMALS: The amount has more decimal places than the currency has.
  • INVALID_BPS: slippageBps or feeBps is out of range (basis points: 100 = 1%).
  • INVALID_QUERY: A query parameter is out of range or malformed; field says which.
  • NOT_A_COIN: The token address is not an ERC-20 on Robinhood Chain.
  • NO_ROUTE: No pool or curve can make this trade right now (none known, or every one failed in simulation; detail lists them).
  • QUOTE_NEEDS_WALLET: This coin’s sale can only be quoted from a wallet that holds it.
  • PRICE_IMPACT_TOO_HIGH: The trade would move the price more than maxPriceImpactPct (default 50%): the pool is too thin for it.
  • SIMULATION_FAILED: The trade failed when simulated again with its minimum in place: the price moved.
  • HONEYPOT: Selling this coin straight after buying it fails through every route, in simulation: it may not be sellable.
  • CHAIN_UNREACHABLE: Robinhood Chain could not be read or simulated just now.
  • MARKET_UNREACHABLE: The coin’s pools could not be read just now.
  • RATE_LIMITED: Too many requests from this address in the last minute.
  • WALLET_REQUIRED: A swap is built for the wallet that will send it.
  • INSUFFICIENT_BALANCE: The wallet holds less of the coin than the sale.
  • INSUFFICIENT_FUNDS: The wallet holds less ETH than the buy.
  • INVALID_BODY: The body is not a JSON object.
  • INVALID_JSON: The body is not valid JSON.