Table of Contents
- Overview
- Use Cases
- Sequence Diagram
The Microchain exchange system is structured similar to Uniswap V2, with some adjustments to fit Fuel architecture and add additional functionality.
Similar to Uniswap, the core functionality is held in a series of "pool" contract. Each contract holds liquidity for two tokens,allows liquidity to be added and removed, and swaps to be performed. User-facing transactions are typically routed through a "router" contract, which adds additional safeguards for trades (such as slippage checks) and allows multi-pool trades.
Unlike Uniswap, which as a factory smart contract responsible for deploying new pools, Microchain uses a "registry" contract. Users deploy new pools independently, but must register them with the registry for them to become "official" pools.
Microchain adds a protocol-fee, which can decrease linearly over time. This fee is dictated by a "vault" contract, but is stored internally by each pool. The pool contract is responsible for collecting these fees, which can be withdrawn back to the vault contract.
The pool contract is the core contract that custodies liquidity and enables trading. The primary functions of this contract are swapping between 2 assets, as well as managing the liquidity provided in the pool. The pool contract also captures price observations, which can be used for a time-weighted average price oracle (TWAP).
This contract is derived from the Uniswap V2 "Pair" contract.
Called once by the contract deployer to initialize the vault value
Requirements
- If the contract has not yet been initialized
Note: While the contract deployer may specify any vault, the "registry" contract will only accept pools that are initialized with the specified vault.
Reads the fee value from the vault contract, and stores the fee values in this contract's state. This allows the contract to calculate the current vault swap fee, without needing to make an external contract call
Adds liquidity to the pool using the untracked assets held by the pool contract, and mints new LP tokens to the recipient
Requirements
- If additional amounts of both tokens have been sent to the pool
Redeems LP tokens for the share of underlying assets in the pool
Requirements
- The sender has sent LP tokens to the contract
- The amount of both assets redeemed is greater than 0 (can't redeem dust amounts of LP tokens)
Swaps one asset in the pool for the other asset. The caller must transfer the input asset to the pool, setting an output value of "0" for the input asset, and the calculated output amount for the output asset. The output asset will be transferred to the recipient.
Requirements
- There is sufficient liquidity in the pool to facilitate the swap
- Sufficient input assets have been transferred to the pool
- The input & output amounts (with the fees factored) maintain the pool invariant balance
Fees
There are 2 fees charged in a given swap:
- A 0.3% fee for liquidity providers
- A variable fee for the protocol, which is defined by the vault
Increases the size of the storage buffer for the time-weighted average price oracle (TWAP).
Withdraws the protocol fees that have been collected by the pool to a given address.
Requirements
The function is called by the vault.
Returns the basic state of the pool: the amount of both tokens held in reserve, and the total supply of LP tokens.
Returns all information related to the vault:
- The address of the vault contract
- The current amount of un-withdrawn tokens collected as protocol fees
- Cached fee data (the current fee, change rate, and the last time updated)
Returns data related to the current protocol fee captured in trades (the current fee, change rate, and the last time updated).
Returns metadata about the pool's time-weighted average price oracle (TWAP), such as the current and future size of the TWAP buffer, and the current position in the buffer queue.
Returns the addresses of the two tokens held by this pool.
Note: The token addresses should always be sorted, where token0 > token1. This is not enforced by the exchange contract, but rather enforced by the registry contract.
Returns a single "observation", consisting of the cumulative token prices, as well as the timestamp of the observation. Two observations can be used to calculate the time-weighted average price over a period of time.
The router contract is the user-facing interface for interacting with the pool contracts. It allows simple swaps and multi-hop swaps, as well as the ability to add & remove liquidity from any pool.
add_liquidity(pool: b256, amount_0_desired: u64, amount_1_desired: u64, amount_0_min: u64, amount_1_min: u64, recipient: Identity) -> LiquidityOutput;
Adds liquidity to a given pool, and returns the minted LP tokens to the recipients. Specifies the required amount of tokens to deposit, as well as the minimum accepted tokens for slippage protection.
The function will return the amount of liquidity added to the pool, as well as the amount of LP tokens sent to the recipient.
Note: since users can not include multiple assets in a contract call, they must transfer both tokens to the router contract before calling add_liquidity
. Any leftover assets will be returned to the caller, not to the recipient.
Requirements
- The specified pool exists and conforms to the standard "Pool" interface
- Sufficient tokens have been sent to the router contract
- The reserve ratio of the pool has not changed sufficiently, causing the amount of input assets to drop below the specified minimums
Removes liquidity from a pool, accepting the LP token from that pool and returning the corresponding tokens to the recipient address.
Requirements
- The call includes LP tokens from the desired pool, which conforms to the standard "Pool" interface
- The reserve ratio of the pool has not changed sufficiently, causing the amount of removed assets to drop below the specified minimums
Swaps one token for another token, via a single pool. The input amount is specified by the amount of tokens included in the contract call. The output tokens are sent to the "recipient" address.
Requirements
- The call includes one of the two tokens included in the pool
- The calculated output (based on input amount, pool reserves & fees) is greater than or equal to the specified minimum output
swap_exact_output(pool: b256, amount_out: u64, max_amount_in: u64, recipient: Identity) -> SwapOutput;
Swaps one token for another token, via a single pool. The output tokens are sent to the "recipient" address.
Requirements
- The call includes one of the two tokens included in the pool
- The calculated input (based on pool reserves & fees) is less than or equal to the specified minimum input
- The calculated input (based on pool reserves & fees) is less than or equal to the amount of tokens included in the call
swap_exact_input_multihop(pools: Vec<b256>, min_amount_out: u64, recipient: Identity) -> SwapOutput;
Swaps one token for another token, via a multiple pools. The input amount is specified by the amount of tokens included in the contract call. The output tokens are sent to the "recipient" address.
Requirements
- The call includes one of the two tokens included in the first pool
- All subsequent pools include the output token of the previous pool (example, pool 0 holds token A & B, pool 1 holds tokens B & C, and pool 2 hols tokens C & D)
- The calculated output (based on input amount, pool reserves & fees) is greater than or equal to the specified minimum output
swap_exact_output_multihop(pools: Vec<b256>, amount_out: u64, max_amount_in: u64, recipient: Identity) -> SwapOutput;
Swaps one token for another token, via a multiple pools. The output tokens are sent to the "recipient" address.
Requirements
- The call includes one of the two tokens included in the first pool
- All subsequent pools include the output token of the previous pool (example, pool 0 holds token A & B, pool 1 holds tokens B & C, and pool 2 hols tokens C & D)
- The calculated input (based on output amount, pool reserves & fees) is less than or equal to the specified minimum input
- The calculated input (based on output amount, pool reserves & fees) is less than or equal to the amount of tokens included in the call
This helper function does nothing.
Given that it is not possible to include more than one asset in a contract call, we must pre-transfer tokens. Having a stub function makes this easy.
Typical EVM AMMs such as Uniswap use a "factory pattern", where any user may create a new pool by calling a function on a factory contract. This factory contract ensures that certain criteria are met (such as only 1 pool per token pair, and an immutable set of code for the contract). These factories also act as registries of all pools that have been deployed.
However, Fuel does not support this "factory pattern". Instead, users wishing to create a new pool will send a simple contract deployment transaction to create the pool. A "registry" contract stores the "official" list of poolsa, and enforces certain guidelines (described in the add_pool_contract
section).
Initializes the registry by storing the code hash of a pool and the pool's vault address. All pools that are added to the registry must have matching code hashes and vault addresses.
Requirements
- The contract has not yet been initialized
Stores a new pool in the registry.
Requirements
- The contract code of the pool matches the contract passed to the
initialize
function - The address of
token0
in the pool is less than the address oftoken1
in the pool - No pools have been added to the registry with the pairing of token0 & token1.
- The pool's LP token supply is 0, meaning the pool hasn't yet been initialized
Returns the address of the registered pool for two given tokens. The order of tokens passed to this function doesn't matter. Will return None
if no registered pool matches the two tokens.
Returns true
if the provided address is a registered pool, otherwise returns false
.
The "code root" of the pool contract passed as a template to initialize
. In other words, the hash of the contract code of a pool contract, which all new pools must match.
The address of the vault contract that all pools are expected to have.
All pools will collect a variable fee from every trade, which is allocated to the protocol treasury. The "vault" contract is responsible for setting the fee rates, and has the ability to withdraw collected fees from the pools.
Gets the current information regarding fee rates. This is composed of the following components:
start_time
: The timestamp that the fee was updatedstart_fee
: The fee at the start timechange_rate
: The rate at which the fee should decrease every second.
These 3 values allow the fee to decrease linearly over time.
The unit for fees is 1,000,000 = 100%. So for example, a start_fee of 10,000 means that the starting fee is 1%.
Updates the fee and the rate at which the fee changes.
Requirements
- Must be called by the vault administrator. If not administrator is set, this function will set the administrator role.
Claims fees from a pool contract, to the vault contract.
This can be called by any account, to any pool.
Withdraws the full balance of a token, to the provided recipient.
Requirements
- Must be called by the vault administrator.