Operations
This page is also Markdown, for agents: /intear-dex/guides/operations.md, and /llms.txt lists every page.
Operations are steps that run one after another in a single call: swaps, withdrawals, transfers, DEX calls, registrations and storage deposits. If one fails, the whole call fails and nothing changes.
There are two ways to run them, and they differ in whose assets they use:
execute_operations |
Operations in a deposit | |
|---|---|---|
| How | Call execute_operations on the engine |
Put them in msg of ft_transfer_call, nft_transfer_call, mt_transfer_call or mt_batch_transfer_call, or in operations of deposit_near |
| Assets used | Your inner balance | Only the assets of this deposit |
| What’s left at the end | Stays in your inner balance | Must be zero, or the call fails |
RegisterAssets, DeployDexCode |
Allowed | Not allowed |
See Operation for the fields of every operation.
The near CLI examples on this page run as alice.near after a storage deposit and registering near, nep141:usdt.tether-token.near and USDC. Together they’re one script.
execute_operations
Section titled “execute_operations”This deposits 1 NEAR, swaps all NEAR in the inner balance for USDt, and withdraws the USDt to the wallet, in one call:
AMOUNT_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": "near", "asset_out": "nep141:usdt.tether-token.near", "amount": {"ExactIn": "1000000000000000000000000"} }' \ network-config mainnet now | jq -r '.[1]')MIN_AMOUNT_OUT=$((AMOUNT_OUT * 99 / 100))
# Deposits the attached 1 NEAR, swaps all NEAR in the inner balance for USDt,# and withdraws the USDtnear contract call-function as-transaction dex.intear.near execute_operations \ json-args "$(jq -n --arg min_amount_out "$MIN_AMOUNT_OUT" '{ "operations": [ { "SwapSimple": { "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAA3QAAAAA=", "asset_in": "near", "asset_out": "nep141:usdt.tether-token.near", "amount": "EntireBalanceIn", "constraint": $min_amount_out } }, { "Withdraw": { "asset_id": "nep141:usdt.tether-token.near", "amount": "PreviousSwapOutput", "to": null, "rescue_address": null } } ] }')" \ prepaid-gas '150.0 Tgas' attached-deposit '1 NEAR' \ sign-as alice.near network-config mainnet sign-with-keychain sendAt least 1 yoctoNEAR must be attached. If you attach more, the whole deposit is added to your NEAR inner balance before the operations run, so you can deposit NEAR and use it in the same call. NEAR must be registered for you in that case.
referrer is optional and applies to every swap and DEX call in the list. referral_fees is optional too: the referral fees of the first swap in the list.
Operations in a deposit
Section titled “Operations in a deposit”With a non-empty msg, a deposit doesn’t go to your inner balance. Instead, the deposited assets are put aside for this call, and the operations can only use them. This is how you deposit, swap and withdraw in one transaction.
This sends USDt with ft_transfer_call, swaps it for exactly 0.1 NEAR, and withdraws both the NEAR and the USDt the swap didn’t use. msg is the list of operations as a JSON string, so it’s built with jq first:
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]')# Send up to 1% more than the simulation. What the swap doesn't use comes back.MAX_AMOUNT_IN=$((AMOUNT_IN * 101 / 100))
# Swaps USDt for exactly 0.1 NEAR, then withdraws the NEAR and the USDt that's leftMSG=$(jq -nc '[ { "SwapSimple": { "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAA3QAAAAA=", "asset_in": "nep141:usdt.tether-token.near", "asset_out": "near", "amount": {"Amount": {"ExactOut": "100000000000000000000000"}}, "constraint": null } }, { "Withdraw": { "asset_id": "near", "amount": "PreviousSwapOutput", "to": null, "rescue_address": null } }, { "Withdraw": { "asset_id": "nep141:usdt.tether-token.near", "amount": {"Full": {"at_least": null}}, "to": null, "rescue_address": null } }]')
near contract call-function as-transaction usdt.tether-token.near ft_transfer_call \ json-args "$(jq -n --arg amount "$MAX_AMOUNT_IN" --arg msg "$MSG" '{ "receiver_id": "dex.intear.near", "amount": $amount, "msg": $msg }')" \ prepaid-gas '300.0 Tgas' attached-deposit '1 yoctoNEAR' \ sign-as alice.near network-config mainnet sign-with-keychain sendWith an exact output, the amount you send is already the most you pay, so the swap doesn’t need a constraint.
msg is either the list of operations, or an object with a referrer, referral fees for the first swap, or both:
{ "operations": [ ... ], "referrer": "myapp.near", "referral_fees": [["myapp.near", 5000]] }For NEAR, pass the same list or object as operations to deposit_near, as JSON rather than a string, with the NEAR attached.
Rules for operations in a deposit:
- Everything must be used. At the end, every asset put aside must be at zero: swapped, withdrawn, transferred or attached to a DEX call. Otherwise the call fails with
Sandboxed assets must be empty after execution. Did you forget to withdraw ...?. With anExactOutswap, withdraw the rest of the input too. - On failure, the deposit goes back. The call fails as a whole, and the token contract returns the tokens to the sender.
- No registrations or deployments.
RegisterAssetsandDeployDexCodefail withOperation only available in execute_actions. - The sender is the one the token contract reports. For NEP-141 it’s
sender_id. For NFTs and multi tokens,sender_idmust equalprevious_owner_id, so tokens sent with an approval can’t run operations. - One token per multi token transfer. An
mt_batch_transfer_callof several tokens can’t run operations, and fails withOperations can only follow a transfer of one token. - DEXs know the difference. A DEX called from a deposit sees the sender as alleged, along with the asset that was deposited, and can refuse actions that need the trader’s permission. See DEXs on the engine.
Withdrawing from a deposit
Section titled “Withdrawing from a deposit”A Withdraw in a deposit needs a place to refund the assets if the transfer fails. The engine refunds to an inner balance, in this order:
- the receiver (
to), if the asset is registered for them, - the sender, if the asset is registered for them,
rescue_address, if the sender doesn’t have the asset registered.
When the operation runs, the sender must have the asset registered, or rescue_address must be set and have it registered. Otherwise it fails with No rescue address provided and user doesn't have a registered balance for this asset, or with Asset <asset> is not registered for Account(<rescue_address>) if rescue_address doesn’t have it.
Into the inner balance
Section titled “Into the inner balance”To deposit, swap and keep the output in the engine, use a TransferAsset to yourself instead of a Withdraw. TransferAsset needs an exact amount, so this works best with an ExactOut swap. This swaps NEAR for exactly 1 USDt, keeps the USDt in the inner balance, and sends the rest of the NEAR back:
# Swaps the attached NEAR for exactly 1 USDt, keeps the USDt in the inner# balance, and withdraws the NEAR that's leftnear contract call-function as-transaction dex.intear.near deposit_near \ json-args '{ "operations": [ { "SwapSimple": { "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAA3QAAAAA=", "asset_in": "near", "asset_out": "nep141:usdt.tether-token.near", "amount": {"Amount": {"ExactOut": "1000000"}}, "constraint": null } }, { "TransferAsset": { "to": {"Account": "alice.near"}, "asset_id": "nep141:usdt.tether-token.near", "amount": "1000000" } }, { "Withdraw": { "asset_id": "near", "amount": {"Full": {"at_least": null}}, "to": null, "rescue_address": null } } ] }' \ prepaid-gas '150.0 Tgas' attached-deposit '1 NEAR' \ sign-as alice.near network-config mainnet sign-with-keychain sendBest of several DEXs
Section titled “Best of several DEXs”SwapAdvanced swaps on one of several options, each a DEX and the message to pass to it, like the dex_id and message of a SwapSimple. The options can be different DEXs, or different routes on the same DEX. The engine simulates the swap on them in order, and with mode:
FindBestswaps on the option that gives the most output for an exact input, or takes the least input for an exact output. Theconstraintapplies to that option.FallbackQueueswaps on the first option that works and meets theconstraint, without trying the ones after it. Put the option you prefer first, and the others as fallbacks.
An option whose simulation fails is skipped, with a log of Skipping swap option on <dex id>: <error>. If no option is left, the operation fails with All swap options failed. An option on a DEX that doesn’t exist fails the whole operation, and so does a picked option whose DEX doesn’t hold enough asset_out. The engine doesn’t run the DEX again for the option it picks: the simulation is made real.
This deposits 0.1 NEAR and swaps it for USDC in whichever of two Plach pools gives more:
# Two Plach pools of NEAR and USDC: 85 and 22OUT_85=$(near contract call-function as-read-only dex.intear.near simulate_swap_simple \ json-args '{ "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAAVQAAAAA=", "trader": "alice.near", "asset_in": "near", "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", "amount": {"ExactIn": "100000000000000000000000"} }' \ network-config mainnet now | jq -r '.[1]')OUT_22=$(near contract call-function as-read-only dex.intear.near simulate_swap_simple \ json-args '{ "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAAFgAAAAA=", "trader": "alice.near", "asset_in": "near", "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", "amount": {"ExactIn": "100000000000000000000000"} }' \ network-config mainnet now | jq -r '.[1]')# Accept up to 1% less than the better of the twoMIN_AMOUNT_OUT=$(( (OUT_85 > OUT_22 ? OUT_85 : OUT_22) * 99 / 100 ))
# Deposits the attached 0.1 NEAR and swaps it for USDC in the pool that gives# more, keeping the USDC in the inner balancenear contract call-function as-transaction dex.intear.near execute_operations \ json-args "$(jq -n --arg min_amount_out "$MIN_AMOUNT_OUT" '{ "operations": [ { "SwapAdvanced": { "asset_in": "near", "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", "amount": "EntireBalanceIn", "constraint": $min_amount_out, "options": [ {"dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAAVQAAAAA="}, {"dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAAFgAAAAA="} ], "mode": "FindBest" } } ] }')" \ prepaid-gas '200.0 Tgas' attached-deposit '0.1 NEAR' \ sign-as alice.near network-config mainnet sign-with-keychain sendReferral fees are paid once, for the option that swaps, and only if it’s the first swap of the list, as with SwapSimple.
Amounts that depend on earlier steps
Section titled “Amounts that depend on earlier steps”SwapSimple and SwapAdvanced take an amount of:
| Value | Meaning |
|---|---|
{ "Amount": { "ExactIn": "..." } } or { "Amount": { "ExactOut": "..." } } |
A fixed amount |
"OutputOfLastIn" |
Exact input of the output of the last swap before it. Its asset_out must be this asset_in |
"EntireBalanceIn" |
Exact input of everything you have of asset_in: your inner balance with execute_operations, what’s left of the deposit with operations in a deposit |
Withdraw takes an amount of:
| Value | Meaning |
|---|---|
{ "Exact": "..." } |
A fixed amount |
{ "Full": { "at_least": "..." } } |
Everything you have of the asset. Fails if it’s less than at_least, which can be null |
"PreviousSwapOutput" |
The output of the last swap before it. Its asset_out must be this asset_id |
“The last swap” is the most recent SwapSimple or SwapAdvanced in the list, even if other operations come after it.
The whole script
Section titled “The whole script”#!/usr/bin/env bash# Runs operations: on the inner balance with execute_operations, and on the# tokens of a deposit. alice.near holds USDt in the wallet and has never used# Intear DEX.set -euo pipefail
near contract call-function as-transaction dex.intear.near storage_deposit \ json-args '{}' \ prepaid-gas '30.0 Tgas' attached-deposit '0.01 NEAR' \ sign-as alice.near network-config mainnet sign-with-keychain send
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 send
AMOUNT_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": "near", "asset_out": "nep141:usdt.tether-token.near", "amount": {"ExactIn": "1000000000000000000000000"} }' \ network-config mainnet now | jq -r '.[1]')MIN_AMOUNT_OUT=$((AMOUNT_OUT * 99 / 100))
# Deposits the attached 1 NEAR, swaps all NEAR in the inner balance for USDt,# and withdraws the USDtnear contract call-function as-transaction dex.intear.near execute_operations \ json-args "$(jq -n --arg min_amount_out "$MIN_AMOUNT_OUT" '{ "operations": [ { "SwapSimple": { "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAA3QAAAAA=", "asset_in": "near", "asset_out": "nep141:usdt.tether-token.near", "amount": "EntireBalanceIn", "constraint": $min_amount_out } }, { "Withdraw": { "asset_id": "nep141:usdt.tether-token.near", "amount": "PreviousSwapOutput", "to": null, "rescue_address": null } } ] }')" \ prepaid-gas '150.0 Tgas' attached-deposit '1 NEAR' \ sign-as alice.near network-config mainnet sign-with-keychain send
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]')# Send up to 1% more than the simulation. What the swap doesn't use comes back.MAX_AMOUNT_IN=$((AMOUNT_IN * 101 / 100))
# Swaps USDt for exactly 0.1 NEAR, then withdraws the NEAR and the USDt that's leftMSG=$(jq -nc '[ { "SwapSimple": { "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAA3QAAAAA=", "asset_in": "nep141:usdt.tether-token.near", "asset_out": "near", "amount": {"Amount": {"ExactOut": "100000000000000000000000"}}, "constraint": null } }, { "Withdraw": { "asset_id": "near", "amount": "PreviousSwapOutput", "to": null, "rescue_address": null } }, { "Withdraw": { "asset_id": "nep141:usdt.tether-token.near", "amount": {"Full": {"at_least": null}}, "to": null, "rescue_address": null } }]')
near contract call-function as-transaction usdt.tether-token.near ft_transfer_call \ json-args "$(jq -n --arg amount "$MAX_AMOUNT_IN" --arg msg "$MSG" '{ "receiver_id": "dex.intear.near", "amount": $amount, "msg": $msg }')" \ prepaid-gas '300.0 Tgas' attached-deposit '1 yoctoNEAR' \ sign-as alice.near network-config mainnet sign-with-keychain send
# Swaps the attached NEAR for exactly 1 USDt, keeps the USDt in the inner# balance, and withdraws the NEAR that's leftnear contract call-function as-transaction dex.intear.near deposit_near \ json-args '{ "operations": [ { "SwapSimple": { "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAA3QAAAAA=", "asset_in": "near", "asset_out": "nep141:usdt.tether-token.near", "amount": {"Amount": {"ExactOut": "1000000"}}, "constraint": null } }, { "TransferAsset": { "to": {"Account": "alice.near"}, "asset_id": "nep141:usdt.tether-token.near", "amount": "1000000" } }, { "Withdraw": { "asset_id": "near", "amount": {"Full": {"at_least": null}}, "to": null, "rescue_address": null } } ] }' \ prepaid-gas '150.0 Tgas' attached-deposit '1 NEAR' \ sign-as alice.near network-config mainnet sign-with-keychain send
# Two Plach pools of NEAR and USDC: 85 and 22OUT_85=$(near contract call-function as-read-only dex.intear.near simulate_swap_simple \ json-args '{ "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAAVQAAAAA=", "trader": "alice.near", "asset_in": "near", "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", "amount": {"ExactIn": "100000000000000000000000"} }' \ network-config mainnet now | jq -r '.[1]')OUT_22=$(near contract call-function as-read-only dex.intear.near simulate_swap_simple \ json-args '{ "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAAFgAAAAA=", "trader": "alice.near", "asset_in": "near", "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", "amount": {"ExactIn": "100000000000000000000000"} }' \ network-config mainnet now | jq -r '.[1]')# Accept up to 1% less than the better of the twoMIN_AMOUNT_OUT=$(( (OUT_85 > OUT_22 ? OUT_85 : OUT_22) * 99 / 100 ))
# Deposits the attached 0.1 NEAR and swaps it for USDC in the pool that gives# more, keeping the USDC in the inner balancenear contract call-function as-transaction dex.intear.near execute_operations \ json-args "$(jq -n --arg min_amount_out "$MIN_AMOUNT_OUT" '{ "operations": [ { "SwapAdvanced": { "asset_in": "near", "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", "amount": "EntireBalanceIn", "constraint": $min_amount_out, "options": [ {"dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAAVQAAAAA="}, {"dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAAFgAAAAA="} ], "mode": "FindBest" } } ] }')" \ prepaid-gas '200.0 Tgas' attached-deposit '0.1 NEAR' \ sign-as alice.near network-config mainnet sign-with-keychain send