# Operations

> Run several steps in one call on Intear DEX: execute_operations on your inner balance, operations in the msg of a token deposit to deposit, swap and withdraw in one transaction, and SwapAdvanced to swap on the best of several DEXs.

This page is part of the Intear DEX docs. Intear DEX is the `dex.intear.near` contract. https://docs.intear.tech/intear-dex/llms-full.txt has all of its pages in one file, and https://docs.intear.tech/llms.txt lists every page of the Intear docs.

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`](https://docs.intear.tech/intear-dex/reference/types.md#operation) for the fields of every operation.

The [near CLI](https://docs.intear.tech/intear-dex/quickstart.md#before-you-start) examples on this page run as alice.near after a [storage deposit](https://docs.intear.tech/intear-dex/guides/storage.md#accounts) and [registering](https://docs.intear.tech/intear-dex/guides/inner-balances.md#register-assets) `near`, `nep141:usdt.tether-token.near` and USDC. Together they're [one script](#the-whole-script).

## `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:

```sh
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 USDt
near 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
```

The whole file, tested in CI: https://docs.intear.tech/snippets/cli/operations.sh

At 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](https://docs.intear.tech/intear-dex/guides/referral-fees.md) of the first swap in the list.

## 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:

```sh
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 left
MSG=$(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
```

The whole file, tested in CI: https://docs.intear.tech/snippets/cli/operations.sh

With 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](https://docs.intear.tech/intear-dex/guides/referral-fees.md) for the first swap, or both:

```json
{ "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 an `ExactOut` swap, 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.** `RegisterAssets` and `DeployDexCode` fail with `Operation 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_id` must equal `previous_owner_id`, so tokens sent with an approval can't run operations.
- **One token per multi token transfer.** An `mt_batch_transfer_call` of several tokens can't run operations, and fails with `Operations 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](https://docs.intear.tech/intear-dex/guides/dexes.md#what-a-dex-sees).

### 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:

1. the receiver (`to`), if the asset is registered for them,
2. the sender, if the asset is registered for them,
3. `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

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:

```sh
# Swaps the attached NEAR for exactly 1 USDt, keeps the USDt in the inner
# balance, and withdraws the NEAR that's left
near 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
```

The whole file, tested in CI: https://docs.intear.tech/snippets/cli/operations.sh

## 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`:

- `FindBest` swaps on the option that gives the most output for an exact input, or takes the least input for an exact output. The `constraint` applies to that option.
- `FallbackQueue` swaps on the first option that works and meets the `constraint`, 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:

```sh
# Two Plach pools of NEAR and USDC: 85 and 22
OUT_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 two
MIN_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 balance
near 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
```

The whole file, tested in CI: https://docs.intear.tech/snippets/cli/operations.sh

[Referral fees](https://docs.intear.tech/intear-dex/guides/referral-fees.md) 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

`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

```sh
#!/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 USDt
near 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 left
MSG=$(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 left
near 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 22
OUT_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 two
MIN_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 balance
near 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
```
