Swaps
A swap trades one asset for another on one DEX. The DEX decides the amounts, the engine moves the assets: amount_in from the trader to the DEX, amount_out from the DEX to the trader.
The near CLI examples on this page swap USDT from alice.near’s inner balance, after a storage deposit, these registrations, and a deposit of 10 USDT. Together they’re one script.
near contract call-function as-transaction dex.intear.near register_assets \ json-args '{ "asset_ids": [ "near", "nep141:usdt.tether-token.near", "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1" ] }' \ prepaid-gas '30.0 Tgas' attached-deposit '1 yoctoNEAR' \ sign-as alice.near network-config mainnet sign-with-keychain sendSimulate
Section titled “Simulate”simulate_swap_simple is a view method that runs a swap without saving anything. It takes the same arguments as swap_simple plus trader, the account the DEX should see as the trader, and without constraint. It returns [amount_in, amount_out].
This one asks how much USDT it takes to get exactly 0.1 NEAR, and allows 1% more for the swap:
AMOUNT_IN=$(near contract call-function as-read-only dex.intear.near simulate_swap_simple \ json-args '{ "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAA3QAAAAA=", "trader": "alice.near", "asset_in": "nep141:usdt.tether-token.near", "asset_out": "near", "amount": {"ExactOut": "100000000000000000000000"} }' \ network-config mainnet now | jq -r '.[0]')# Pay up to 1% more than the simulationMAX_AMOUNT_IN=$((AMOUNT_IN * 101 / 100))The simulation doesn’t check balances or registrations, so a swap that simulates fine can still fail for those reasons.
swap_simple
Section titled “swap_simple”near contract call-function as-transaction dex.intear.near swap_simple \ json-args "$(jq -n --arg max_amount_in "$MAX_AMOUNT_IN" '{ "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAA3QAAAAA=", "asset_in": "nep141:usdt.tether-token.near", "asset_out": "near", "amount": {"ExactOut": "100000000000000000000000"}, "constraint": $max_amount_in }')" \ prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' \ sign-as alice.near network-config mainnet sign-with-keychain send| Argument | Description |
|---|---|
dex_id |
The DEX to swap on |
message |
Base64 bytes passed to the DEX as is. Each DEX defines its own format, usually the pool or route. For Plach, AQAAAEBCDwABAAAA3QAAAAA= is pool 221 (NEAR/USDT) and AQAAAEBCDwABAAAAVQAAAAA= is pool 85 (NEAR/USDC). See Plach routes |
asset_in, asset_out |
The assets to swap |
amount |
{"ExactIn": "<amount>"} to pay an exact amount, or {"ExactOut": "<amount>"} to receive an exact amount |
constraint |
Optional. With ExactIn, the minimum amount_out. With ExactOut, the maximum amount_in. The swap fails if it’s not met |
referrer |
Optional. Passed to the DEX, and logged in the swap event. Plach only logs it: its referral fees are in the message. Pass the same referrer to the simulation, in case a DEX uses it |
It returns [amount_in, amount_out].
Before swapping:
- Your inner balance must have at least
amount_inofasset_in. asset_outmust be registered for you, or the output can’t be credited.
If anything fails, including the constraint, the whole call fails and no balances change.
What the engine checks
Section titled “What the engine checks”The DEX returns amount_in and amount_out. The engine then checks that:
- with
ExactIn, the DEX’samount_inequals the requested amount (Amount in does not matchotherwise), - with
ExactOut, the DEX’samount_outequals the requested amount (Amount out does not matchotherwise), - the
constraintholds, - the DEX has enough
asset_outin its inner balance.
The DEX can’t take more than amount_in from you or give out tokens it doesn’t hold.
Several swaps in one call
Section titled “Several swaps in one call”To swap through several pools or DEXs, use execute_operations with several SwapSimple operations. Each one can take the output of the previous one as input with "amount": "OutputOfLastIn".
This swaps 1 USDT to USDC through NEAR, with two Plach pools. To get the least USDC to accept, simulate each swap with the output of the one before:
NEAR_OUT=$(near contract call-function as-read-only dex.intear.near simulate_swap_simple \ json-args '{ "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAA3QAAAAA=", "trader": "alice.near", "asset_in": "nep141:usdt.tether-token.near", "asset_out": "near", "amount": {"ExactIn": "1000000"} }' \ network-config mainnet now | jq -r '.[1]')USDC_OUT=$(near contract call-function as-read-only dex.intear.near simulate_swap_simple \ json-args "$(jq -n --arg near_out "$NEAR_OUT" '{ "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAAVQAAAAA=", "trader": "alice.near", "asset_in": "near", "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", "amount": {"ExactIn": $near_out} }')" \ network-config mainnet now | jq -r '.[1]')MIN_USDC_OUT=$((USDC_OUT * 99 / 100))near contract call-function as-transaction dex.intear.near execute_operations \ json-args "$(jq -n --arg min_usdc_out "$MIN_USDC_OUT" '{ "operations": [ { "SwapSimple": { "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAA3QAAAAA=", "asset_in": "nep141:usdt.tether-token.near", "asset_out": "near", "amount": {"Amount": {"ExactIn": "1000000"}}, "constraint": null } }, { "SwapSimple": { "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAAVQAAAAA=", "asset_in": "near", "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", "amount": "OutputOfLastIn", "constraint": $min_usdc_out } } ] }')" \ prepaid-gas '200.0 Tgas' attached-deposit '1 yoctoNEAR' \ sign-as alice.near network-config mainnet sign-with-keychain sendPut the constraint on the last swap: it limits the result of the whole chain. The intermediate assets must be registered for you, because each output is credited to your inner balance before the next swap takes it.
Plach can also route through several of its pools in one swap, with a longer route in message. Several SwapSimple operations are for routes that go through different DEXs, or when you build the route yourself.