Skip to content

Swaps

A Plach swap is an Intear DEX swap 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, simulate_swap_simple, and SwapSimple operations, with exact input or exact output.

The examples on this page swap from alice.near’s inner balance, after a storage deposit, registering NEAR, USDT and USDC, and depositing 1 NEAR and 1 USDT. Together they’re one script.

The message is a Borsh SwapArgs: a list of routes, an optional fee discount, and referral fees.

struct SwapArgs {
routes: Vec<SwapRoute>,
fee_discount: Option<SignedFeeDiscount>,
/// Left out when empty
referral_fees: ReferralFees,
}
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>,
}
/// Accounts and their fees, 1000000 = 100%
struct ReferralFees(Vec<(AccountId, u32)>);

Without referral fees, the message ends after fee_discount, as it did before they existed.

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

Terminal window
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

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:

Terminal window
# 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

Unlike several SwapSimple operations, 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.

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
Terminal window
# 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

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

The pool ID 4294967295 (u32::MAX, ffffffff) means the pool created last. Use it to create a pool and swap in it in one execute_operations, before you know its ID.

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 the least amount that gives at least amount_out after fees. 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. See Fees for what they are and where they go.

Each pool a swap goes through logs a Swap event with its amounts and fees, and a PoolUpdated event with its new balances.