Concentrated Liquidity Pool Integration

Purpose

Guide on how to Build Transaction Swap Concentrated Pool Token:

Constants

Preprod:

POOL_SCRIPT_HASH="04041c3c6ba87b33f2c9eb7f7dbeae3b26003c3e199d438bb99932a2"
POOL_SCRIPT_OUT_REF="2e19cca74e3badcab26aef7574aa1885ba97228a254ca227ba2f79f2b75fd136#0"
PROTOCOL_SCRIPT_HASH="26ec271e96420bd548932f350e76ea38590da68e12f0f55bd5473f67"
PROTOCOL_CONFIG_OUT_REF="3775af36f485f9c97101ee5b9b360c34f0f8e12186bc9060f358b7fc8ce468a4#0"

Mainnet:

POOL_SCRIPT_HASH="d8b69fc53637bcfadbc4469083f706bc293f4d9d2296646c5ca167bb"
POOL_SCRIPT_OUT_REF="64d111b957e7d7848ffdde5149aa77fa4090a7fa1ad0ac108067900614848501#0"
PROTOCOL_SCRIPT_HASH="fa991bc2f9c4206e72d713bc3487a72e7901057cabb8d364bebeef8f"
PROTOCOL_CONFIG_OUT_REF="2cafd7c92f7093e5229af274be83dea660b0590b4174bbed79ba662b44fbd1ee#0"

Steps to Build a Transaction Swap Token

Step 1: Retrieve Necessary Parameters

Mainnet API:

Preprod API:

Mainnet API:

Preprod API:

Step 2: Build the Redeemer Structure

Concentrated ExchangeActionRedeemer Structure:

To optimize data size and allow one transaction (txn) to swap across multiple pools simultaneously, the Redeemer is a ByteArray, where each field has a defined index and byte size.

  • ExchangeActionRedeemer structure for Token Swap:

  • Diagnostic Notation format:

  • CBOR format (single pool):

Step 3: Create the Transaction

Use the retrieved parameters to create the token swap transaction.

  • Note: To minimize DDoS attacks in multi-hop pools, each pool, when swapped, will collect ADA fees according to protocol_config.swap_fee and add them to the Pool Datum:

  • Amount of swapFee to be charged and current totalSwapFee in Pool Datum is returned by API.

1

Validity range

Set the validity range for the transaction.

  • Ensure the time-to-live (TTL) <=6 minutes.

2

Input

  • Note: The outRef, address, coin, and multiAssets information is fully returned by API Get Params.

    • PoolInUtxo: The pool you intend to swap tokens with.

      • Spend Redeemer: Build the redeemer according to the ExchangeActionRedeemer Swap structure for Concentrated Pool, ensuring the specified indices are correct.

      • The input Pool UTxO must have the valid PoolNFT:

        • pool_in_asset[PoolNFT] = 1

3

Output

  • PoolOutUtxo: The output of the pool after the swap.

    • delta_amount > 0 -> Trader sells X, Pool receives X and pays Y. Simultaneously increases platform_fee_X accumulation.

      • poolChangeX = redeemer.delta_amount

    • delta_amount < 0 -> Trader sells Y, Pool receives Y and pays X. Simultaneously increases platform_fee_Y accumulation.

      • poolChangeY = -redeemer.delta_amount

    • The pool datum structure must adhere to the correct order:

  • Temporary variables:

    • basis = 10000

    • pool_utxo_min_ada = 3_000_000 (lovelace)

    • poolinX: tokenAReserve from the API

    • poolinY: tokenBReserve from the API

    • poolinLPX: liquidity of token X before the txn

      • if token X is ADA: = poolinX - pool_in_datum.platform_fee_x - pool_in_datum.total_swap_fee - pool_utxo_min_ada

      • else: = poolinX - pool_in_datum.platform_fee_x

    • poolinLPY: liquidity of token Y before the txn = pool_in_asset[Y] - pool_in_datum.platform_fee_y

    • Pa=pool_in_datum.sqrt_lower_price \sqrt{P_a} = pool\_in\_datum.sqrt\_lower\_price

    • Pb=pool_in_datum.sqrt_upper_price \sqrt{P_b} = pool\_in\_datum.sqrt\_upper\_price

    • L=CEIL(poolinLPY+poolinLPXPaPb+(poolinLPYpoolinLPXPaPb)2+4poolinLPXpoolinLPYPb2(PbPa))L=CEIL(\frac{poolinLPY + poolinLPX\sqrt{P_a}\sqrt{P_b} + \sqrt{(poolinLPY-poolinLPX\sqrt{P_a}\sqrt{P_b})^2 + 4poolinLPXpoolinLPY*P_b}}{2(\sqrt{P_b}-\sqrt{P_a})})

    • Xv=poolinLPX+CEIL(LPb)X_v=poolinLPX+CEIL(\frac{L}{\sqrt{P_b}})

    • Yv=poolinLPY+CEIL(LPa)Y_v=poolinLPY+CEIL(L\sqrt{P_a})

    • offFee=1pool_in_datum.lp_fee_rate/basisoffFee = 1-pool\_in\_datum.lp\_fee\_rate/basis

  • Constraints:

    • |poolChangeX| >= pool_in_datum.min_x_change

    • |poolChangeY| >= pool_in_datum.min_y_change

    • IF pool_in_datum.token_x == ""

      • Required withdrawal of the rewards by the pool's stake credential (before this action)

    • if poolChangeX > 0:

      • pool_out_datum.platform_fee_x=FLOOR(pool_in_datum.platform_fee_x+pool_in_datum.lp_fee_ratebasisplatform_fee_ratebasispoolChangeX)pool\_out\_datum.platform\_fee\_x = FLOOR(pool\_in\_datum.platform\_fee\_x + \frac{pool\_in\_datum.lp\_fee\_rate}{basis}*\frac{platform\_fee\_rate}{basis}*poolChangeX)

      • pool_out_datum.platform_fee_y=pool_in_datum.platform_fee_ypool\_out\_datum.platform\_fee\_y = pool\_in\_datum.platform\_fee\_y

      • poolChangeY>=CEIL(XvYv/(Xv+poolChangeXoffFee)Yv)poolChangeY >= CEIL(X_v*Y_v / (X_v + poolChangeX * offFee) - Y_v)

    • else:

      • pool_out_datum.platform_fee_x=pool_in_datum.platform_fee_xpool\_out\_datum.platform\_fee\_x = pool\_in\_datum.platform\_fee\_x

      • pool_out_datum.platform_fee_y=FLOOR(pool_in_datum.platform_fee_y+pool_in_datum.lp_fee_ratebasisplatform_fee_ratebasispoolChangeY)pool\_out\_datum.platform\_fee\_y = FLOOR(pool\_in\_datum.platform\_fee\_y + \frac{pool\_in\_datum.lp\_fee\_rate}{basis}*\frac{platform\_fee\_rate}{basis}*poolChangeY)

      • poolChangeX>=CEIL(XvYv/(Yv+poolChangeYoffFee)Xv)poolChangeX >= CEIL(X_v*Y_v / (Y_v + poolChangeY * offFee) - X_v)

      • pool_out_datum.last_withdraw_epoch = curEpoch

      • pool_out_datum.total_swap_fee = pool_in_datum.total_swap_fee + protocol_config_ref.swap_fee

    • Other fields in datum are not changed:

      • pool_out_datum.lp_fee_rate = pool_in_datum.lp_fee_rate

      • pool_out_datum.token_x = pool_in_datum.token_x

      • pool_out_datum.token_y = pool_in_datum.token_y

      • pool_out_datum.sqrt_lower_price = pool_in_datum.sqrt_lower_price

      • pool_out_datum.sqrt_upper_price = pool_in_datum.sqrt_upper_price

      • pool_out_datum.circulating_lp_token = pool_in_datum.circulating_lp_token

    • Pool UTxO's address must not be changed

  • Diagnostic Notation format:

4

Reference Input

  • Add all reference inputs:

    • pool_script_out_ref: current smart contract's out ref is 64d111b957e7d7848ffdde5149aa77fa4090a7fa1ad0ac108067900614848501#0

    • protocol_config_out_ref: returned by the API, field pool.protocolConfigOutRef

    • If withdraw staking, add staking_script_out_ref: returned by the API, field stakingUtxo.outRef

5

Withdrawal

  • Withdraw Pool Script Hash:

    • Add withdraw-zero: Add withdrawal to reward address of Pool Script Hash with reward amount = 0

      • Note: Pool Script Hash can be extracted using lpToken's policy ID from the API

    • Redeemer: Built according to the ExchangeActionRedeemer structure.

  • Withdraw Staking Reward: Must add staking withdrawal if pool contains ADA and current_epoch > pool_datum.last_withdraw_epoch.

    • Check reward amount of pool script hash and add withdrawal to its reward account.

    • Redeemer: Built according to the ExchangeActionRedeemer structure.

Note: After sorting the inputs and reference inputs according to the chain's sort order, ensure the correct indices are used when specifying them in the redeemers.

We've built a typescript SDK for your reference: Herearrow-up-right

Last updated