MarketlabMarketlabDocs

Pools

Create public liquidity pools on Elysium and run dynamic swap fees.

Use mlab pool to create public liquidity pools with dynamic swap fees on Elysium. Anyone can create a pool; its creator becomes the manager. Other users can add liquidity after the manager's first deposit.

Want to sell a token allocation without supplying a second asset? Use a bonding market instead.

Two parts work together:

PartWhat it does
Pool contract on ElysiumHolds the tokens, executes swaps and tracks LP shares. Swap fees stay in the pool for LPs.
Fee controller in mlabdObserves the pool's price movement and submits fee changes within the manager's fixed bounds.

The pool uses constant-product pricing (x * y = k), the model used by Uniswap v2. It is a custom Market Lab contract, not a deployment of Uniswap v4 or its dynamic-fee hooks.

Creating a pool does not start dynamic fees. You authorize an operator and start mlab pool run separately. If that job stops, swaps and withdrawals still work. Swaps use the last applied fee; the manager can also change it directly.

First-Time Setup

Don't have a token yet? Create a fixed-supply test token to try the full workflow. Existing projects can use their own token instead.

  1. Choose and fund your pair. Hold both ERC-20 tokens on Elysium, plus native HYPE for gas. Wrap HYPE if your pair needs it. Creating a pool does not mint these tokens.
  2. Create the pool. Prepare the transaction, sign it with your wallet and confirm the new pool address.
  3. Add liquidity. Sign the token approvals and initial deposit. The pool can now serve swaps at its starting fee.
  4. Authorize an operator. Create its key, sign its approval and fund it with native HYPE.
  5. Start dynamic fees. Preview the policy, then start the daemon job.

To only provide liquidity, use an existing pool and follow step 3. You do not need to create an operator or run someone else's fee controller.

Create a Pool

Choose Your Tokens

For example, create a USDC/WHYPE pool on Elysium testnet (99801) using these deployed contracts:

TokenElysium contract addressDecimals
Bridged test USDC0x7D29d8047B905000459c0E80c34a26CeedcB47b26
Example WHYPE wrapper0xA5b72796E5bf80443DC134F3c8a1cc97dcCC39B918

These are examples, not required assets. You can pair your own standard ERC-20 token with WHYPE, or use two other tokens deployed on Elysium. Creating a pool does not create its tokens. Copy token contract addresses from the project's deployment or Elysium explorer, not their symbols or addresses on another chain.

The USDC above is the bridge representation of Circle's HyperEVM testnet USDC, not native Circle-issued USDC on Elysium. It has no real dollar value. To obtain it, use the Elysium bridge from HyperEVM testnet; the source token there is 0x2B3370eE501B4a559b57D449569354196457D8Ab. See token bridging for the address mapping.

Prepare the Transaction

Connect your wallet to Elysium testnet and keep native HYPE for gas. The faucet provides test HYPE.

Replace the example --account address below with your wallet's public address. Keep the two token addresses to use the example pair, or replace them with your own Elysium token contracts.

mlab pool create \
  --account 0x0361c685F6bA3Cd66e9e8fE6095C91Ab0b4e5b8E \
  --token-a 0x7D29d8047B905000459c0E80c34a26CeedcB47b2 \
  --token-b 0xA5b72796E5bf80443DC134F3c8a1cc97dcCC39B9 \
  --fee-bps 30 --min-fee-bps 5 --max-fee-bps 100 \
  --output json

Starting fee: 0.30%. Fixed bounds: 0.05% to 1%.

Wallet actions prepare unsigned transactions. Submit them with your wallet or signing tool. They do not sign, broadcast or fund the pool for you.

Sign and Submit

The creation result contains transaction.to, data, value, from and chainId. Sign with the wallet matching from on Elysium testnet.

For example, with Foundry, use your local keystore name in place of elysium. If needed, import your wallet locally with cast wallet import elysium --interactive. Replace the three TRANSACTION_... fields with the returned JSON values:

cast send TRANSACTION_TO --data TRANSACTION_DATA --value TRANSACTION_VALUE \
  --rpc-url https://testnet-rpc.elysium.kinetiq.xyz --account elysium

This command signs and broadcasts. Wait for a successful receipt. For a deposit or swap that returns multiple transactions, submit each entry's transaction in order and wait for success before continuing.

After signing and submitting, replace CREATION_TX_HASH with your transaction hash. Use the same wallet address:

mlab pool created CREATION_TX_HASH \
  --account 0x0361c685F6bA3Cd66e9e8fE6095C91Ab0b4e5b8E --output json

Returns pending, then confirmed with the pool address after two successor blocks. This is not guaranteed finality. Resume with the same hash if interrupted; another creation transaction creates another pool.

Add Liquidity

Replace POOL_ADDRESS with the pool returned by your creation transaction. Check mlab pool inspect POOL_ADDRESS before depositing: amounts follow token0/token1 address order, not the order you typed during creation.

For the exact example pair above, token0 is USDC and token1 is WHYPE. This prepares 1 test USDC + 0.01 WHYPE; replace the example wallet with your own:

mlab pool deposit POOL_ADDRESS \
  --account 0x0361c685F6bA3Cd66e9e8fE6095C91Ab0b4e5b8E \
  --amount0 1000000 --amount1 10000000000000000 \
  --slippage-bps 50 --output json

Amounts use integer base units: 1 USDC with 6 decimals is 1000000; 0.01 WHYPE with 18 decimals is 10000000000000000. Choose your own deposit sizes. Changing either token contract can change the token order and decimals.

Use Sign and Submit for the returned approvals and deposit. Wait for each successful receipt; do not continue if a transaction fails. See Pool JSON for the output fields.

Only the manager can make the first deposit, which sets the starting price. The example ratio sets 1 WHYPE = 100 test USDC; it is an illustration, not a market-price quote. LP shares go to the depositing wallet; swap fees stay in the reserves for LPs.

Default slippage: 0.50%. Expiry: 20 minutes. Preparing again skips sufficient approvals. Check any already-sent deposit's receipt before retrying.

Authorize a Fee Operator

Create a dedicated operator on the machine running mlabd:

mlab pool operator create
mlab pool operator address

The key is saved in $MLAB_HOME/credentials/pool-operator.key (default ~/.market-lab), with 0600 permissions, not encryption. Only the public address is printed.

Replace OPERATOR_ADDRESS with the public address printed by mlab pool operator address:

mlab pool authorize POOL_ADDRESS \
  --account 0x0361c685F6bA3Cd66e9e8fE6095C91Ab0b4e5b8E \
  --operator OPERATOR_ADDRESS --output json

Sign and submit the returned transaction with the pool manager wallet. Then send native HYPE to the operator address for fee-update gas. The operator can change swap fees, not withdraw other users' LP assets. No fee job has started yet.

Start Dynamic Fees

Once the pool has liquidity and the operator is approved and funded, preview and start:

mlab pool run POOL_ADDRESS --base-fee-bps 30 --dry-run
mlab pool run POOL_ADDRESS --base-fee-bps 30

--dry-run reads and previews only. The second command asks for confirmation and starts the job in mlabd; use --yes for unattended execution. Your daemon must keep running to update fees.

Check its status and recent fee decisions, or stop it using the returned job ID:

mlab pool jobs
mlab pool logs "JOB_ID" --limit 20
mlab pool stop "JOB_ID"

How the Fee Changes

The controller samples the pool's reserve ratio, measures recent price movement and calculates:

fee = base fee + sensitivity × measured volatility

With a 30 bps base and sensitivity 1, a measured volatility of 20 bps gives a 50 bps (0.50%) fee, if the pool's bounds allow it. As volatility falls, the target returns toward the base. This is periodic adjustment, not a new calculation on every swap, and does not guarantee higher LP returns.

Volatility is the RMS log return of the pool's reserve ratio, in bps per observation, not an external price feed. The target is rounded and clamped to the contract's fixed bounds. The controller waits for the cooldown and minimum-change threshold before submitting another update.

FlagDefault
--base-fee-bps30
--sensitivity1
--window20 observations
--interval30 seconds
--cooldown300 seconds
--min-change-bps2
--max-tx-gas-hype0.0001 HYPE per transaction

Jobs continue after the CLI exits and resume after daemon restarts. One running fee job is allowed per pool.

Stopping prevents new fee decisions; a transaction already prepared may still settle. It does not close the pool or block swaps and withdrawals. The last applied swap fee stays in effect until the manager or an authorized operator changes it.

To revoke permission, prepare pool authorize with --operator 0x0000000000000000000000000000000000000000 and sign it with the manager wallet.

Use --output json or --output jsonl for structured results. Operator commands print an address only.

Find and Inspect Pools

mlab pool list --limit 10 --offset 0
mlab pool inspect POOL_ADDRESS

The list shows factory pools newest first, up to 50 per page. Replace POOL_ADDRESS with an address from it. Inspection shows the token pair, reserves, decimals, LP supply, fee bounds and operator permissions. No wallet is required.

LP tokens share the name MarketLab Pool Share (MLP). Identify pools by address and token pair; custom names are not supported.

Swap or Withdraw

Read your LP shares, withdrawable amounts and wallet balances:

mlab pool position POOL_ADDRESS \
  --account 0x0361c685F6bA3Cd66e9e8fE6095C91Ab0b4e5b8E --output json

For the example pair, swap 0.1 test USDC. For withdrawals, replace LP_SHARES_RAW with the shares.raw amount from your position result. Use your own pool and wallet:

mlab pool swap POOL_ADDRESS \
  --account 0x0361c685F6bA3Cd66e9e8fE6095C91Ab0b4e5b8E \
  --token-in 0x7D29d8047B905000459c0E80c34a26CeedcB47b2 \
  --amount-in 100000 --output json

mlab pool withdraw POOL_ADDRESS \
  --account 0x0361c685F6bA3Cd66e9e8fE6095C91Ab0b4e5b8E \
  --shares LP_SHARES_RAW --output json

Both return a quote and unsigned transactions for your wallet. Swaps include approvals when needed; withdrawals burn your LP shares and return both assets. Use --slippage-bps to change the default 50 bps; plans expire after 20 minutes.

Get Wrapped HYPE

Skip this section if you already hold your chosen wrapper's tokens. Native HYPE pays gas; WHYPE is the ERC-20 asset deposited into a pool. Get test HYPE from the Elysium faucet.

There is no mlab pool wrap command. The commands below use Foundry and a local keystore named elysium. Use an existing keystore, or import a wallet locally with cast wallet import elysium --interactive.

Wrap 0.01 HYPE in the example contract:

cast send 0xA5b72796E5bf80443DC134F3c8a1cc97dcCC39B9 "deposit()" \
  --value 10000000000000000 \
  --rpc-url https://testnet-rpc.elysium.kinetiq.xyz \
  --account elysium

This command signs and broadcasts, returning 0.01 WHYPE to that wallet. Keep some native HYPE unwrapped for gas.

Deploy Your Own Wrapper

The listed WHYPE address is Market Lab's example deployment, not a required or canonical Elysium wrapper. You can deploy the same WrappedHype contract yourself. From contracts/pools in the Market Lab repository, with Foundry installed:

make deps
forge create src/WrappedHype.sol:WrappedHype \
  --rpc-url https://testnet-rpc.elysium.kinetiq.xyz \
  --account elysium --broadcast

Use the printed Deployed to address instead of the example WHYPE address when wrapping and creating your pool. Each wrapper has separate balances; they are not interchangeable just because both use the symbol WHYPE.

Create a Test Token

mlab token create prepares a fixed-supply ERC-20 deployment on Elysium testnet. You choose the name, symbol and supply. All tokens go to the wallet passed as --account; there is no later minting. Supply is entered in whole-token units, not base units. Tokens use 18 decimals.

mlab token create \
  --name "Example Token" \
  --symbol EXAMPLE \
  --supply 1000000 \
  --account 0x0361c685F6bA3Cd66e9e8fE6095C91Ab0b4e5b8E \
  --output json

Replace the example account with your wallet's public address. Hold native HYPE for deployment gas. The result contains an unsigned contract-creation transaction: your wallet signs and sends it. There is no to address because this deploys a new contract. The CLI never asks for a private key or broadcasts this plan.

After signing, use mlab token created <transaction-hash> --account <your-wallet-address> --output json. It reports pending until three confirmations, then returns the verified token address, metadata and supply. Receipt checks reject deployments from another wallet or contracts with different runtime bytecode. Both commands accept --rpc-url for another endpoint on the same chain.

Use the returned token address with mlab pool create. For example, from a supply of 1,000,000 tokens you could deposit 200,000 tokens alongside 10 WHYPE, keeping 800,000 tokens in your wallet. This transfers existing tokens into the pool and mints LP shares; it does not create more of your token. Then authorize the fee operator and start dynamic fees as above.

Cloud's temporary Launch a test token entry provides the same steps through your browser wallet, including pool creation, initial liquidity and automatic-fee setup. Tokens and existing pools remain onchain if the launcher is later disabled.

On this page