Skip to content

Pools

A pool holds two different assets, NEAR, NEP-141 or NEP-245, and swaps between them. Pool IDs are counted from 0 in the order pools were created.

Anyone can add liquidity and gets shares. A public pool is created with its first liquidity, and the creator gets 1018 shares for it, so nobody else can set its starting price. If all its liquidity is removed, though, the next one to add liquidity sets the price again. Its fees can never change. Its Pool fees go to its liquidity providers, each in the way it chooses.

The owner, who created it, is the only one who adds and removes liquidity, and can change the fees. The owner can lock the pool, which is final: after that, its liquidity can’t be removed and its fees can’t change. Locking a pool shows traders and holders of a token that its liquidity stays.

For launching a token. The creator gives the pool all of the new token, and sets an amount of phantom liquidity of the quote asset (often NEAR): liquidity that isn’t there, but sets the starting price as if it were. As people buy, real quote asset comes in. A swap that would take the pool’s quote balance below the phantom liquidity fails, so sellers can only take out quote asset that buyers put in.

Launch pools don’t take or give back liquidity after creation, and their fees can’t change. An Account receiver with ConvertToFirstAsset is paid in the quote asset, sells included, and a Pool fee stays in the pool. See The asset a fee is paid in.

create_pool takes:

struct CreatePoolArgs {
assets: (AssetId, AssetId),
fees: FeeConfiguration,
pool_type: PoolType,
/// Required for public pools, optional for private pools,
/// not accepted for launch pools
initial_liquidity: Option<(u128, u128)>,
/// How the creator takes its part of the pool fees, as the provider of
/// the first liquidity. Required for public pools, not accepted for others
creator_fee_payout_type: Option<FeePayoutType>,
}

See Types for FeeConfiguration, PoolType and FeePayoutType, Fees for what a fee configuration can do, and Liquidity for the payout of the creator’s part of a public pool’s fees.

Attach:

  • NEAR for storage, always. What’s left after the storage is sent to your wallet.
  • The initial liquidity, exactly. If one of the assets is NEAR, its amount comes out of the same attached NEAR as the storage.
  • For a launch pool, the launched token (the second asset), all of it. The first asset is the quote asset, and pool_type has the phantom liquidity.

Both assets must be registered for Plach in the engine, or the call fails with Asset <asset> is not registered for Dex(slimedragon.near/xyk). NEAR and USDt already are, but a new token, like any launched one, isn’t: check with are_assets_registered and register it with register_assets, both with "for": {"Dex": "slimedragon.near/xyk"}.

The response is the new pool’s ID, a Borsh u32. This creates a private pool of NEAR and USDt with a 0.3% fee for the pool, of which 0.24% stays in it and 0.06% is the protocol’s, and 1 NEAR and 5 USDt of liquidity:

00 asset 0: near
01 16 00 00 00 "usdt.tether-token.near" asset 1: nep141:usdt.tether-token.near
01 00 00 00 fees: 1 receiver
01 Pool
00 b8 0b 00 00 Fixed(3000), 0.3%
00 pool type: PrivateLatest
01 initial liquidity: Some
00 00 00 a1 ed cc ce 1b c2 d3 00 00 00 00 00 00 1000000000000000000000000 (1 NEAR)
40 4b 4c 00 00 00 00 00 00 00 00 00 00 00 00 00 5000000 (5 USDt)
00 creator's fee payout: None, since the pool is private

It runs after a storage deposit, registering NEAR and USDt, and depositing 1.01 NEAR and 5 USDt, as in this script. near --quiet prints only the result, so the pool ID can be kept for the next calls:

# A private pool of NEAR and USDt with a 0.3% fee that stays in the pool, and
# 1 NEAR and 5 USDt of liquidity. The other 0.01 NEAR pays for its storage.
# --quiet prints only the result: the ID of the new pool, in Borsh and base64.
POOL_ID=$(near --quiet contract call-function as-transaction dex.intear.near dex_call \
json-args '{
"dex_id": "slimedragon.near/xyk",
"method": "create_pool",
"args": "AAEWAAAAdXNkdC50ZXRoZXItdG9rZW4ubmVhcgEAAAABALgLAAAAAQAAAKHtzM4bwtMAAAAAAABAS0wAAAAAAAAAAAAAAAAAAA==",
"attached_assets": {
"near": "1010000000000000000000000",
"nep141:usdt.tether-token.near": "5000000"
}
}' \
prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' \
sign-as alice.near network-config mainnet sign-with-keychain send | jq -r .)
echo "Created pool $(echo "$POOL_ID" | base64 -d | od -An -tu4)"

get_pool takes a pool ID and returns a Borsh Option<PoolView>, None if there’s no such pool. The pool ID that create_pool returned is already the whole argument:

# The pool ID in Borsh is also the whole argument of get_pool
near contract call-function as-read-only dex.intear.near dex_view \
json-args "$(jq -n --arg pool_id "$POOL_ID" '{
"dex_id": "slimedragon.near/xyk",
"method": "get_pool",
"args": $pool_id
}')" \
network-config mainnet now

A PoolView has the pool’s kind, assets and balances, its fees with the protocol fee, and its fee configuration. get_pools returns several, from start_index, and get_pool_count the number of pools. The ID of each pool in a page is start_index plus its position. See Methods.

The owner of a private pool can replace its fee configuration with edit_fees, which takes the pool ID followed by the new configuration. Attach NEAR if the new configuration takes more storage than the old one, like a new receiver, which the pool registers for the assets its payout takes, or a scheduled fee in place of a fixed one. Plach uses what it needs and sends the rest to your wallet. This sets the fee to 0.1%, which takes the same storage, so nothing is attached:

# The pool ID, followed by the new fee: 0.1% that stays in the pool
ARGS=$({ echo "$POOL_ID" | base64 -d; echo "AQAAAAEA6AMAAA==" | base64 -d; } | base64)
near contract call-function as-transaction dex.intear.near dex_call \
json-args "$(jq -n --arg args "$ARGS" '{
"dex_id": "slimedragon.near/xyk",
"method": "edit_fees",
"args": $args,
"attached_assets": {}
}')" \
prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' \
sign-as alice.near network-config mainnet sign-with-keychain send

lock_pool takes the pool ID. Only the owner can lock a pool, and it can’t be unlocked.

near contract call-function as-transaction dex.intear.near dex_call \
json-args "$(jq -n --arg pool_id "$POOL_ID" '{
"dex_id": "slimedragon.near/xyk",
"method": "lock_pool",
"args": $pool_id,
"attached_assets": {}
}')" \
prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' \
sign-as alice.near network-config mainnet sign-with-keychain send

create_pool also takes the pool types of older versions, like PublicV2 or LaunchV1, and creates a pool of the current version of its kind. Use the Latest types. LaunchV1 still requires NEAR as the first asset.

Every existing pool was upgraded to the current version, so there’s no upgrade_pool or pool_needs_upgrade any more.

#!/usr/bin/env bash
# Creates a private NEAR/USDt pool on Plach, changes its fee, and locks it.
# 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"]}' \
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.01 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": "5000000", "msg": ""}' \
prepaid-gas '50.0 Tgas' attached-deposit '1 yoctoNEAR' \
sign-as alice.near network-config mainnet sign-with-keychain send
# A private pool of NEAR and USDt with a 0.3% fee that stays in the pool, and
# 1 NEAR and 5 USDt of liquidity. The other 0.01 NEAR pays for its storage.
# --quiet prints only the result: the ID of the new pool, in Borsh and base64.
POOL_ID=$(near --quiet contract call-function as-transaction dex.intear.near dex_call \
json-args '{
"dex_id": "slimedragon.near/xyk",
"method": "create_pool",
"args": "AAEWAAAAdXNkdC50ZXRoZXItdG9rZW4ubmVhcgEAAAABALgLAAAAAQAAAKHtzM4bwtMAAAAAAABAS0wAAAAAAAAAAAAAAAAAAA==",
"attached_assets": {
"near": "1010000000000000000000000",
"nep141:usdt.tether-token.near": "5000000"
}
}' \
prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' \
sign-as alice.near network-config mainnet sign-with-keychain send | jq -r .)
echo "Created pool $(echo "$POOL_ID" | base64 -d | od -An -tu4)"
# The pool ID in Borsh is also the whole argument of get_pool
near contract call-function as-read-only dex.intear.near dex_view \
json-args "$(jq -n --arg pool_id "$POOL_ID" '{
"dex_id": "slimedragon.near/xyk",
"method": "get_pool",
"args": $pool_id
}')" \
network-config mainnet now
# The pool ID, followed by the new fee: 0.1% that stays in the pool
ARGS=$({ echo "$POOL_ID" | base64 -d; echo "AQAAAAEA6AMAAA==" | base64 -d; } | base64)
near contract call-function as-transaction dex.intear.near dex_call \
json-args "$(jq -n --arg args "$ARGS" '{
"dex_id": "slimedragon.near/xyk",
"method": "edit_fees",
"args": $args,
"attached_assets": {}
}')" \
prepaid-gas '100.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 dex_call \
json-args "$(jq -n --arg pool_id "$POOL_ID" '{
"dex_id": "slimedragon.near/xyk",
"method": "lock_pool",
"args": $pool_id,
"attached_assets": {}
}')" \
prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' \
sign-as alice.near network-config mainnet sign-with-keychain send