# Swaps

> Swapping on Plach: the Borsh swap message with its routes and fee discount, single-pool, multi-pool and split routes, and how prices are computed.

This page is part of the Plach docs. Plach is the DEX `slimedragon.near/xyk` on the Intear DEX contract, `dex.intear.near`, and every call goes to that contract. https://docs.intear.tech/plach/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.

A Plach swap is an Intear DEX [swap](https://docs.intear.tech/intear-dex/guides/swaps.md) with `"dex_id": "slimedragon.near/xyk"`. The `message` of the swap says which pools to go through. Everything else works as on any DEX of the engine: [`swap_simple`](https://docs.intear.tech/intear-dex/guides/swaps.md#swap_simple), [`simulate_swap_simple`](https://docs.intear.tech/intear-dex/guides/swaps.md#simulate), and `SwapSimple` [operations](https://docs.intear.tech/intear-dex/guides/operations.md), with exact input or exact output.

The examples on this page swap from alice.near's inner balance, after a [storage deposit](https://docs.intear.tech/intear-dex/guides/storage.md#accounts), [registering](https://docs.intear.tech/intear-dex/guides/inner-balances.md#register-assets) NEAR, USDt and USDC, and [depositing](https://docs.intear.tech/intear-dex/guides/inner-balances.md#deposit) 1 NEAR and 1 USDt. Together they're [one script](#the-whole-script).

## The message

The message is a Borsh `SwapArgs`: a list of routes, and an optional [fee discount](https://docs.intear.tech/plach/guides/fees.md#fee-discounts).

```rust
struct SwapArgs {
    routes: Vec<SwapRoute>,
    fee_discount: Option<SignedFeeDiscount>,
}

struct SwapRoute {
    /// Share of the amount that goes through this route, 1000000 = 100%
    fraction: u32,
    /// Pools to swap through, in order
    pool_ids: Vec<u32>,
}
```

[Referral fees](https://docs.intear.tech/intear-dex/guides/referral-fees.md) are an argument of the engine's swap, not part of the message. Messages from before they moved there can still list them after `fee_discount`. Plach reads and ignores them, so they don't charge anything.

For a swap of the whole amount through one pool, without a fee discount, the message can also be just the pool ID, a little-endian `u32`. `3QAAAA==` is pool 221, NEAR/USDt:

```sh
near contract call-function as-read-only dex.intear.near simulate_swap_simple \
  json-args '{
    "dex_id": "slimedragon.near/xyk",
    "message": "3QAAAA==",
    "trader": "alice.near",
    "asset_in": "near",
    "asset_out": "nep141:usdt.tether-token.near",
    "amount": {"ExactIn": "1000000000000000000000000"}
  }' \
  network-config mainnet now
```

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

### Through several pools

Each pool of a route swaps the output of the one before into its other asset, and the last one must output `asset_out`. This message goes from USDt to NEAR in pool 221, then from NEAR to USDC in pool 85:

```
01 00 00 00    1 route
40 42 0f 00      fraction: 1000000 (100%)
02 00 00 00      2 pools
dd 00 00 00        221
55 00 00 00        85
00             no fee discount
```

That's `AQAAAEBCDwACAAAA3QAAAFUAAAAA` in base64:

```sh
# USDt to NEAR in pool 221, then NEAR to USDC in pool 85
AMOUNT_OUT=$(near contract call-function as-read-only dex.intear.near simulate_swap_simple \
  json-args '{
    "dex_id": "slimedragon.near/xyk",
    "message": "AQAAAEBCDwACAAAA3QAAAFUAAAAA",
    "trader": "alice.near",
    "asset_in": "nep141:usdt.tether-token.near",
    "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
    "amount": {"ExactIn": "1000000"}
  }' \
  network-config mainnet now | jq -r '.[1]')
MIN_AMOUNT_OUT=$((AMOUNT_OUT * 99 / 100))

near contract call-function as-transaction dex.intear.near swap_simple \
  json-args "$(jq -n --arg min_amount_out "$MIN_AMOUNT_OUT" '{
    "dex_id": "slimedragon.near/xyk",
    "message": "AQAAAEBCDwACAAAA3QAAAFUAAAAA",
    "asset_in": "nep141:usdt.tether-token.near",
    "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
    "amount": {"ExactIn": "1000000"},
    "constraint": $min_amount_out
  }')" \
  prepaid-gas '150.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/plach-swaps.sh

Unlike several `SwapSimple` [operations](https://docs.intear.tech/intear-dex/guides/swaps.md#several-swaps-in-one-call), the intermediate asset (NEAR here) doesn't go through your inner balance, so it doesn't need to be registered for you, and it's one swap for the engine.

### Split across routes

With several routes, each takes its `fraction` of the amount, and the fractions must add up to 1000000. The first route also takes what's left after rounding. This one swaps 70% of 1 NEAR to USDC in pool 85, and 30% in pool 22:

```
02 00 00 00    2 routes
60 ae 0a 00      fraction: 700000 (70%)
01 00 00 00      1 pool
55 00 00 00        85
e0 93 04 00      fraction: 300000 (30%)
01 00 00 00      1 pool
16 00 00 00        22
00             no fee discount
```

```sh
# 70% through pool 85 and 30% through pool 22, both NEAR/USDC
AMOUNT_OUT=$(near contract call-function as-read-only dex.intear.near simulate_swap_simple \
  json-args '{
    "dex_id": "slimedragon.near/xyk",
    "message": "AgAAAGCuCgABAAAAVQAAAOCTBAABAAAAFgAAAAA=",
    "trader": "alice.near",
    "asset_in": "near",
    "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
    "amount": {"ExactIn": "1000000000000000000000000"}
  }' \
  network-config mainnet now | jq -r '.[1]')
MIN_AMOUNT_OUT=$((AMOUNT_OUT * 99 / 100))

near contract call-function as-transaction dex.intear.near swap_simple \
  json-args "$(jq -n --arg min_amount_out "$MIN_AMOUNT_OUT" '{
    "dex_id": "slimedragon.near/xyk",
    "message": "AgAAAGCuCgABAAAAVQAAAOCTBAABAAAAFgAAAAA=",
    "asset_in": "near",
    "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
    "amount": {"ExactIn": "1000000000000000000000000"},
    "constraint": $min_amount_out
  }')" \
  prepaid-gas '150.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/plach-swaps.sh

Splits work for exact output too: then each route delivers its fraction of `amount_out`.

### The last created pool

The pool ID `4294967295` (`u32::MAX`, `ffffffff`) means the pool created last. Use it to [create a pool](https://docs.intear.tech/plach/guides/pools.md#create-a-pool) and swap in it in one `execute_operations`, before you know its ID.

## Prices

In each pool, the fees are taken from the input first. The rest goes into the pool, and the output keeps the product of the pool's balances:

```
amount_out = in_after_fees * out_balance / (in_balance + in_after_fees)
```

rounded down. For an exact output, the input is worked out backwards from the same formula and rounded up, so exactly `amount_out` comes out after fees. The input can be a few units more than the least that would do. A swap can't take a pool's whole balance of an asset, and fails on an empty pool.

Each pool of a route charges its own fees. Fees paid out in the pool's other asset are swapped into it in the same pool right after, without fees, which moves its price a little more than the formula says. See [Fees](https://docs.intear.tech/plach/guides/fees.md) for what they are and where they go.

## Events

Each pool a swap goes through logs a [`Swap`](https://docs.intear.tech/plach/reference/events.md#swap) event with its amounts and fees, and a [`PoolUpdated`](https://docs.intear.tech/plach/reference/events.md#poolupdated) event with its new balances.

## The whole script

```sh
#!/usr/bin/env bash
# Swaps on Plach with a split route and a route through two pools. 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

near contract call-function as-transaction dex.intear.near deposit_near \
  json-args '{}' \
  prepaid-gas '30.0 Tgas' attached-deposit '1 NEAR' \
  sign-as alice.near network-config mainnet sign-with-keychain send

near contract call-function as-transaction usdt.tether-token.near ft_transfer_call \
  json-args '{"receiver_id": "dex.intear.near", "amount": "1000000", "msg": ""}' \
  prepaid-gas '50.0 Tgas' attached-deposit '1 yoctoNEAR' \
  sign-as alice.near network-config mainnet sign-with-keychain send

near contract call-function as-read-only dex.intear.near simulate_swap_simple \
  json-args '{
    "dex_id": "slimedragon.near/xyk",
    "message": "3QAAAA==",
    "trader": "alice.near",
    "asset_in": "near",
    "asset_out": "nep141:usdt.tether-token.near",
    "amount": {"ExactIn": "1000000000000000000000000"}
  }' \
  network-config mainnet now

# 70% through pool 85 and 30% through pool 22, both NEAR/USDC
AMOUNT_OUT=$(near contract call-function as-read-only dex.intear.near simulate_swap_simple \
  json-args '{
    "dex_id": "slimedragon.near/xyk",
    "message": "AgAAAGCuCgABAAAAVQAAAOCTBAABAAAAFgAAAAA=",
    "trader": "alice.near",
    "asset_in": "near",
    "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
    "amount": {"ExactIn": "1000000000000000000000000"}
  }' \
  network-config mainnet now | jq -r '.[1]')
MIN_AMOUNT_OUT=$((AMOUNT_OUT * 99 / 100))

near contract call-function as-transaction dex.intear.near swap_simple \
  json-args "$(jq -n --arg min_amount_out "$MIN_AMOUNT_OUT" '{
    "dex_id": "slimedragon.near/xyk",
    "message": "AgAAAGCuCgABAAAAVQAAAOCTBAABAAAAFgAAAAA=",
    "asset_in": "near",
    "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
    "amount": {"ExactIn": "1000000000000000000000000"},
    "constraint": $min_amount_out
  }')" \
  prepaid-gas '150.0 Tgas' attached-deposit '1 yoctoNEAR' \
  sign-as alice.near network-config mainnet sign-with-keychain send

# USDt to NEAR in pool 221, then NEAR to USDC in pool 85
AMOUNT_OUT=$(near contract call-function as-read-only dex.intear.near simulate_swap_simple \
  json-args '{
    "dex_id": "slimedragon.near/xyk",
    "message": "AQAAAEBCDwACAAAA3QAAAFUAAAAA",
    "trader": "alice.near",
    "asset_in": "nep141:usdt.tether-token.near",
    "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
    "amount": {"ExactIn": "1000000"}
  }' \
  network-config mainnet now | jq -r '.[1]')
MIN_AMOUNT_OUT=$((AMOUNT_OUT * 99 / 100))

near contract call-function as-transaction dex.intear.near swap_simple \
  json-args "$(jq -n --arg min_amount_out "$MIN_AMOUNT_OUT" '{
    "dex_id": "slimedragon.near/xyk",
    "message": "AQAAAEBCDwACAAAA3QAAAFUAAAAA",
    "asset_in": "nep141:usdt.tether-token.near",
    "asset_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
    "amount": {"ExactIn": "1000000"},
    "constraint": $min_amount_out
  }')" \
  prepaid-gas '150.0 Tgas' attached-deposit '1 yoctoNEAR' \
  sign-as alice.near network-config mainnet sign-with-keychain send
```
