Fees
Fees are parts of a swap’s input. Plach writes them as a u32 out of 1000000, so 3000 is 0.3% and 1000000 would be 100%.
Each pool swap pays, from its input:
- the pool fees, set by the pool’s creator,
- the protocol fee, to Plach,
- referral fees, if the swap’s message lists any.
Each fee is its fraction of the input, rounded down, and each pool of a route charges its own. When a swap sells a launched token in a launch pool, the fees are taken in the token and swapped to the quote asset in the same pool, so they’re always paid in the quote asset.
Pool fees
Section titled “Pool fees”A pool’s fee configuration is a list of receivers and their fees. Version 2, which all new pools can use, is:
enum FeeConfiguration { V1(CurrentFees), V2 { receivers: Vec<(FeeReceiver, FeeAmount)> },}
enum FeeReceiver { /// The fee is collected for this account, which withdraws it Account(AccountId), /// The fee stays in the pool, for its liquidity providers or owner Pool, /// Only in launch pools quoted in NEAR, see "Community fees" Community(AccountId),}
enum FeeAmount { Fixed(u32), /// Goes down from the start fee to the end fee, between the two times Scheduled { start: (u64, u32), end: (u64, u32), curve: ScheduledFeeCurve }, /// Not supported yet Dynamic { min: u32, max: u32 },}Times are in nanoseconds, like NEAR block timestamps, and the only curve is Linear. A scheduled fee must go down: the start time must be before the end time, and the start fee higher than the end fee. Before the start it’s the start fee, after the end it’s the end fee. For example, a launch pool can start with a high fee that goes down over its first hours.
V1(CurrentFees) is a plain list of (FeeReceiver, u32), the format of V1 pools.
Limits, checked when a pool is created and when its fees change:
- at most 20 receivers,
- each fee below 1000000, and all of them together at most 900000 (90%),
plach.intear.near, the protocol fee receiver, can’t be a receiver.
Who can change a pool’s fees depends on its kind: only the owner of an unlocked private pool, with edit_fees.
Protocol fee
Section titled “Protocol fee”Plach adds a protocol fee for plach.intear.near to the pool fees:
| Pool | Protocol fee |
|---|---|
| Both assets are stable | 0.0001% (1) |
| Any other | 0.1% (1000) |
The stable assets are NEAR, USDT (usdt.tether-token.near), USDC (17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1), and tokens of omft.near and omni.hot.tg accounts. Intear may change this list.
While a scheduled fee hasn’t reached its end, 5% of it goes to the protocol fee instead of its receiver.
The fees of a PoolView are the fees of a swap right now, with the protocol fee last.
Collected fees
Section titled “Collected fees”Fees for Account receivers, referral fees and the protocol fee are kept by Plach for each account and asset. get_pending_fees shows them, and withdraw_fees moves them to the account’s inner balance, so the assets must be registered for it there. See the example below.
Referral fees
Section titled “Referral fees”An app can charge its own fees on the swaps it sends, by listing them in the swap’s message as referral_fees: accounts and the fee each one gets, on top of the pool and protocol fees. Nothing is set up in advance, so each swap can charge different fees.
- At most 5 accounts, each listed once, with fees that add up to at most 5% (
50000) in each pool. A fee of 0 charges nothing. - 20% of each referral fee goes to the protocol fee receiver,
plach.intear.near, rounded down, and the rest to the account. - An account is only paid in assets it registered with
register_fee_assets: the input asset of the swap, or the quote asset when the swap sells into a launch pool. Otherwise that account’s fee isn’t charged, and neither is the protocol’s share of it. Registering an asset twice fails. - Fee discounts don’t reduce referral fees.
- The
referrerof a swap is only logged, it doesn’t charge anything.
The Developers page of the Plach app registers fee assets for you, with a list of common ones. The DEX Aggregator puts its referral_fees in the message of Plach swaps, and includes them in its quotes.
With near CLI, the examples below run as alice.near after a storage deposit, registering NEAR and USDT for it in the engine, and depositing 1.02 NEAR. alice.near refers its own swap to keep the example short. Together they’re one script.
# Collect referral fees in NEAR and USDTnear contract call-function as-transaction dex.intear.near dex_call \ json-args '{ "dex_id": "slimedragon.near/xyk", "method": "register_fee_assets", "args": "AgAAAAABFgAAAHVzZHQudGV0aGVyLXRva2VuLm5lYXI=", "attached_assets": {"near": "10000000000000000000000"} }' \ prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' \ sign-as alice.near network-config mainnet sign-with-keychain sendThis message swaps through pool 221 with a referral fee of 1% (10000) for alice.near:
01 00 00 00 1 route40 42 0f 00 fraction: 1000000 (100%)01 00 00 00 1 pooldd 00 00 00 22100 no fee discount01 00 00 00 1 referral fee0a 00 00 00 10 bytes of account ID61 6c 69 63 65 2e 6e 65 61 72 alice.near10 27 00 00 10000 (1%)Simulate it with the same message, since the fee changes the output. alice.near gets 0.8% of the 1 NEAR it sells, and plach.intear.near 0.2%:
# A swap through pool 221 that pays alice.near a referral fee of 1%. Simulate it# with the same message, since the referral fee changes the output.AMOUNT_OUT=$(near contract call-function as-read-only dex.intear.near simulate_swap_simple \ json-args '{ "dex_id": "slimedragon.near/xyk", "message": "AQAAAEBCDwABAAAA3QAAAAABAAAACgAAAGFsaWNlLm5lYXIQJwAA", "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))
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": "AQAAAEBCDwABAAAA3QAAAAABAAAACgAAAGFsaWNlLm5lYXIQJwAA", "asset_in": "near", "asset_out": "nep141:usdt.tether-token.near", "amount": {"ExactIn": "1000000000000000000000000"}, "constraint": $min_amount_out }')" \ prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' \ sign-as alice.near network-config mainnet sign-with-keychain sendWithdraw fees
Section titled “Withdraw fees”get_pending_fees takes an account and a list of assets, and returns a Borsh HashMap<AssetId, u128> of the fees collected in each, for the assets that are registered. Here NEAR, since the swap paid in NEAR:
near contract call-function as-read-only dex.intear.near dex_view \ json-args '{ "dex_id": "slimedragon.near/xyk", "method": "get_pending_fees", "args": "CgAAAGFsaWNlLm5lYXIBAAAAAA==" }' \ network-config mainnet nowwithdraw_fees takes a list of assets, and moves all of the caller’s collected fees in them to their inner balance:
near contract call-function as-transaction dex.intear.near dex_call \ json-args '{ "dex_id": "slimedragon.near/xyk", "method": "withdraw_fees", "args": "AQAAAAA=", "attached_assets": {} }' \ prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' \ sign-as alice.near network-config mainnet sign-with-keychain sendCommunity fees
Section titled “Community fees”A launch pool quoted in NEAR can have Community(account) receivers: fees in NEAR for an account that doesn’t have to do anything to get them, for example the community of the launched token. Anyone can send them to the account’s wallet with withdraw_community_fee, which takes the account ID. get_community_owned_fees shows how much is waiting, in yoctoNEAR.
Fee discounts
Section titled “Fee discounts”A swap message can carry a signed fee discount, which reduces the pool and protocol fees of that swap. The DEX Aggregator signs them for traders holding TEAR.
struct SignedFeeDiscount { discount: FeeDiscount, /// Signature of sha256(borsh(discount)) by the fee discount signer signature: Vec<u8>,}
struct FeeDiscount { routes: Vec<SwapRoute>, asset_in: AssetId, asset_out: AssetId, /// Only this trader can use it, anyone if `None` trader_id: Option<AccountId>, amount: SwapRequestAmount, /// 1000000 waives the pool and protocol fees fee_discount: u32, expiration_block_height: u64,}The discount is only valid for exactly this swap: the same routes, assets and amount, by trader_id (the alleged trader), until the block height. The signature is Ed25519, or secp256k1 with the recovery ID as the last byte, by the key that get_fee_discount_signer returns. If that’s None, fee discounts are off.