Trading Pool



Please refer to our white paper for a more rigorous treatment on the subject.

At ALEX, we build DeFi primitives targeting developers looking to build ecosystem on Bitcoin, enabled by Stacks. As such, we focus on trading, lending and borrowing of crypto assets with Bitcoin as the settlement layer and Stacks as the smart contract layer. At the core of this focus is the automated market making ("AMM") protocol, which allows users to exchange one crypto asset with another trustlessly.

Trading Pool implements Generalised Mean Equation and, with a suitable parameterisation, supports both risky pairs (i.e. xy=Lx y=L), stable pairs (i.e. x+y=Lx +y=L) and any linear combination in-between (i.e. Curve).

Trading Pool is parameterised with a single parameter tt. tt can be between 0 and 1, with t=1t=1 being equivalent of constant product formula (i.e. Uniswap V2) and t=0t=0 being equivalent of constant sum formula (i.e. mStable). 0<t<10<t <1 then gives a Curve-like formula.

Caution on fixed notation

Please note we use 8-digit fixed notation to represent decimals. If you interact directly with any of our contracts, you must provide all numbers in the correct format.

For example, 1 should be passed as 10,000,000 (= 1e8), i.e. 1.00000000.

Pool creation

A pair can be registered (i.e. a pool can be created) by calling create-pool with the parameters including the traits of the two tokens (token-x and token-y), the factor tt, the governance address (pool-owner) and the initial liquidity.

Trading Pool is permission-less in that anyone can register a pair with initial liquidity, so long as the two tokens are pre-approved (this is to prevent introducing malicious tokens to the platform).

Pool governance

Certain privileged functions are available to pool-owner to govern the pool. The pool-owner address is set at the time of a pool creation. ALEX DAO, as part of its governance, has the power to update and replace the pool-owner address. Therefore, you can view this as ALEX DAO delegating the governance of each pool to its respective pool-owner.

  • set-fee-rate-x and set-fee-rate-y: set the swap fee (% of swap amount) of token-x and token-y, respectively. Both fee-rate-x and fee-rate-y are zero by default;

  • set-start-block and set-end-block: set the block heights before and after, respectively, which the pool is not available. Both start-block and end-block is set to u340282366920938463463374607431768211455 by default;

  • set-threshold-x and set-threshold-y: set the amount of token-x and token-y, respectively, below which a minimum % slippage is applied. Both threshold-x and threshold-y are zero by default;

  • set-oracle-enabled: add or remove the pool from the on-chain price oracle. Oracle is disabled by default;

  • set-oracle-average: set the exponential moving average factor for the oracle-resilient. Please note this call will reset the existing oracle-resilient value. The oracle-average is zero by default. We recommend 0.99e8.

Liquidity provision

Liquidity can be added to or removed from the pool any time by calling add-to-position or reduce-position, respectively.

Adding liquidity

When adding liquidity to a pool, you need to specify the amount of token-x and have the option of specifying the maximum amount of token-y you are willing to pair with token-x (i.e. slippage control). If adding liquidity requires more token-y than the maximum you specified, then the call will fail. You must also have at least the amount of token-x and the maximum amount of token-y in your wallet - otherwise the call will fail.

Once the liquidity is added, the pool will mint a pool token as a proof of proportional ownership of the pool liquidity. The number of the pool token being minted is proportional to the amount of liquidity you added compared to the existing liquidity at the pool.

The pool token is transferrable and may be used at other protocols (for example, as a collateral).

Removing liquidity

When removing liquidity from a pool, you need to specify the percentage of your pool tokens that you want to liquidate, i.e. between 0 and 1.

The percentage will be converted to the number of pool tokens to be burnt and the corresponding amount of token-x and token-y will be sent to you.

Pool tokens

The pool token implements SIP013. Each pool is mapped to a unique id (pool-id) with associated liquidity mapped to the balance under that id.

Fee rebates

Trading Pool re-invests any fee rebates to the relevant pool liquidity, i.e. the invariant increases slightly after each transaction, similar to Uniswap V2.


Users can swap one token with another by calling swap-x-for-y or swap-y-for-x. As the names imply, swap-x-for-y swaps token-x into token-y and swap-y-for-x swaps token-y into token-x.

In both cases you can specify your slippage limit (min-dy and min-dx, respectively), so that the call fails if the swapped amount does not meet your target.


Fee is deducted from each transaction on the "in" leg, i.e. token-x for swap-x-for-y and token-y for swap-y-for-x. Fee is set at the pool creation and may be updated through the governance.

Part of the fee may be rebated to liquidity providers as a reward.

Swap helper and routing

It may not be reasonable to expect developers or users to remember the correct order of token pairs. Therefore we provide swap-helper function that helps choose between swap-x-for-y and swap-y-for-x and swaps token-x into token-y without users having to know the correct order.

It is also useful to be able to combine multiple swaps into one. For example, it will be useful to be able to swap xUSD into xBTC in one transaction, based on two pools of STX/xUSD and STX/xBTC, instead of having to perform two swaps. To that end, we provide three helper functions - swap-helper-a, swap-helper-b and swap-helper-c that facilitates "multi-hop" swaps of two/three/four pools, respectively.

Helper functions

In addition to the swap helpers and routing functions, we provide a few helpful functions.


Trading Pool provides two types of on-chain oracles - instant and resilient. Their implementations are similar to Uniswap V2.

Instant oracle (get-oracle-instant) gives you the latest pool-implied price (i.e. most up-to-date), but is subject to a higher manipulation risk. Instant oracle may be suitable for, for example, arbitrage or liquidation.

Resilient oracle (get-oracle-resilient) on the other hand gives you a trade-weighted price that is therefore more resilient to potential manipulation but is less up to date. Resilient oracle may be more suitable for, for example, benchmarking to lending and borrowing.

Out given in

Sometimes you may want to know the expected amount of token-y if you were to swap certain amount of token-x. Or you may want to know the expected amount of token-x for some token-y. Two read-only functions - get-y-given-x and get-x-given-y will do that for you.

In given out

If you want to know the amount of token-x you may need to provide to get a target amount of token-y (or vice versa), you can use get-x-in-given-y-out and get-y-in-give-x-out, respectively.

In given price

If you are an arbitrageur, you may want to know the amount of token-x or token-y you need to provide to rebalance the pool-implied price to a target. In such a case, you can use get-x-given-price or get-y-given-price.

Liquidity provision

Lastly, it will be useful to know, for example, to determine the slippage limit, how many token-x and token-y must be provided to mint certain number of pool tokens, to burn certain number of pool tokens, or how many pool tokens may be minted or burnt if certain number of token-x and token-y are provided. The relevant helper functions are get-position-given-mint, get-position-given-burn and get-token-given-position.

Last updated