Skip to main content

Providing/Withdrawing Liquidity

Overview

A user can provide liquidity to a pool by calling provide_liquidity. Before executing the provide_liqudity operation, a user must allow the pool contract to take tokens from their wallet for all CW20 tokens in a given pair.

Users can also withdraw liquidity by calling withdraw_liquidity. To receive the underlying liquidity back plus accrued LP fees, LPs must burn their liquidity tokens.

Providing Liquidity

increase_allowance

Before we can provide liquidity to a pool, we need to increase the token allowance for all CW20 tokens in the pair.

The increase_allowance endpoint for a given CW20 token contract contains:

  • A spender (the pair address we are depositing to in our example)
  • The amount to increase the allowance by
  • An optional expiration time

_9
{
_9
"increase_allowance": {
_9
"spender": astrolunaAddress,
_9
"amount": "1000000000000",
_9
"expires": {
_9
"never": {}
_9
}
_9
}
_9
}

provide_liquidity

To provide liquidity, you need to execute a contract message pointing to the provide_liquidity endpoint in the pair contract for the selected pool.

assets is the only required parameter and contains information about each token in the pair, including the contract address or native denomination and the amount to deposit.

For new pools, you can deposit any ratio of tokens in a pair. For existing pairs, the contract will throw an error message if the proper balance of ratios between tokens is not met.


_25
{
_25
"provide_liquidity": {
_25
"assets": [
_25
{
_25
"info": {
_25
"token": {
_25
"contract_addr": astroAddress
_25
}
_25
},
_25
"amount": beliefPrice
_25
},
_25
{
_25
"info": {
_25
"native_token": {
_25
"denom": "uluna"
_25
}
_25
},
_25
"amount": "500000"
_25
}
_25
],
_25
"slippage_tolerance": "0.005",
_25
"auto_stake": false,
_25
"receiver": "..."
_25
}
_25
}

Optional Parameters

provide_liqudity takes up the following optional parameters:

  • slippage_tolerance: Allows liquidity provision while the price of assets in a pool stays within a certain range
  • auto_stake: Determines whether the LP tokens minted for the user are auto staked in the Generator contract.
  • receiver: The receiver of LP tokens (if different from sender)

_25
{
_25
"provide_liquidity": {
_25
"assets": [
_25
{
_25
"info": {
_25
"token": {
_25
"contract_addr": astroAddress
_25
}
_25
},
_25
"amount": beliefPrice
_25
},
_25
{
_25
"info": {
_25
"native_token": {
_25
"denom": "uluna"
_25
}
_25
},
_25
"amount": "500000"
_25
}
_25
],
_25
"slippage_tolerance": "0.005",
_25
"auto_stake": false,
_25
"receiver": "..."
_25
}
_25
}

Coins

If the pair you are providing liquidity to contains a native asset, you will also need to send the native token along with the transaction message.

The code in this section is an example of what a provide_liquidity message may look like using feather.js to send Coins with our transaction.


_44
const astroAddress = 'terra167dsqkh2alurx997wmycw9ydkyu54gyswe3ygmrs4lwume3vmwks8ruqnv';
_44
_44
const provideLiquidity = new MsgExecuteContract(
_44
wallet.key.accAddress('terra'),
_44
astrolunaAddress,
_44
{
_44
"provide_liquidity": {
_44
"assets": [
_44
{
_44
"info": {
_44
"token": {
_44
"contract_addr": astroAddress
_44
}
_44
},
_44
"amount": beliefPrice
_44
},
_44
{
_44
"info": {
_44
"native_token": {
_44
"denom": "uluna"
_44
}
_44
},
_44
"amount": "500000"
_44
}
_44
],
_44
"slippage_tolerance": "0.005",
_44
"auto_stake": false
_44
}
_44
},
_44
new Coins({ "uluna": "500000" })
_44
);
_44
_44
_44
const fee = new Fee(2000000, { uluna: 200000 });
_44
_44
const tx = await wallet.createAndSignTx({
_44
msgs: [provideLiquidity],
_44
fee,
_44
chainID: 'pisco-1',
_44
});
_44
_44
const result = await lcd.tx.broadcast(tx, 'pisco-1');
_44
_44
console.log(result);

Calculating Deposit Amount

As noted above, the contract will throw an error message if the proper balance of ratios between tokens is not met.

In order to obtain the ratio of tokens that we need, we can simulate a swap and input our response as the amount for one of our deposits.


_24
{
_24
"provide_liquidity": {
_24
"assets": [
_24
{
_24
"info": {
_24
"token": {
_24
"contract_addr": astroAddress
_24
}
_24
},
_24
"amount": beliefPrice
_24
},
_24
{
_24
"info": {
_24
"native_token": {
_24
"denom": "uluna"
_24
}
_24
},
_24
"amount": "500000"
_24
}
_24
],
_24
"slippage_tolerance": "0.005",
_24
"auto_stake": false
_24
}
_24
}

simulation

The simulation endpoint contains information about our offer asset and the amount to swap.

Our input amount and response will gives us the proper ratio of tokens that we need to provide_liquidity.


_12
{
_12
"simulation": {
_12
"offer_asset": {
_12
"info": {
_12
"native_token": {
_12
"denom": "uluna"
_12
}
_12
},
_12
"amount": "500000"
_12
}
_12
}
_12
}

increase_allowance

Before we can provide liquidity to a pool, we need to increase the token allowance for all CW20 tokens in the pair.

The increase_allowance endpoint for a given CW20 token contract contains:

  • A spender (the pair address we are depositing to in our example)
  • The amount to increase the allowance by
  • An optional expiration time

provide_liquidity

To provide liquidity, you need to execute a contract message pointing to the provide_liquidity endpoint in the pair contract for the selected pool.

assets is the only required parameter and contains information about each token in the pair, including the contract address or native denomination and the amount to deposit.

For new pools, you can deposit any ratio of tokens in a pair. For existing pairs, the contract will throw an error message if the proper balance of ratios between tokens is not met.

Optional Parameters

provide_liqudity takes up the following optional parameters:

  • slippage_tolerance: Allows liquidity provision while the price of assets in a pool stays within a certain range
  • auto_stake: Determines whether the LP tokens minted for the user are auto staked in the Generator contract.
  • receiver: The receiver of LP tokens (if different from sender)

Coins

If the pair you are providing liquidity to contains a native asset, you will also need to send the native token along with the transaction message.

The code in this section is an example of what a provide_liquidity message may look like using feather.js to send Coins with our transaction.

Calculating Deposit Amount

As noted above, the contract will throw an error message if the proper balance of ratios between tokens is not met.

In order to obtain the ratio of tokens that we need, we can simulate a swap and input our response as the amount for one of our deposits.

simulation

The simulation endpoint contains information about our offer asset and the amount to swap.

Our input amount and response will gives us the proper ratio of tokens that we need to provide_liquidity.


_9
{
_9
"increase_allowance": {
_9
"spender": astrolunaAddress,
_9
"amount": "1000000000000",
_9
"expires": {
_9
"never": {}
_9
}
_9
}
_9
}

Withdrawing Liquidity

withdraw_liquidity

To withdraw_liquidity, you need to execute a contract message pointing to the send endpoint in the CW20 contract of the LP token associated with the pair.

send includes the contract the tokens are being sent to (pair contract address), the amount to send (withdraw), and a binary encoded msg containing our contract call (withdraw: {}).


_11
{
_11
"send": {
_11
"contract": astrolunaAddress,
_11
"amount": "10000000",
_11
"msg": toBase64(
_11
{
_11
"withdraw_liquidity": {}
_11
}
_11
)
_11
}
_11
}

Encoding our Msg

To encode our message, there are two common options:

The code in this section uses a custom function (toBase64) to display our binary message - this function needs to be defined elsewhere to be used. The actual string representation of our message would be an encoded binary.


_11
{
_11
"send": {
_11
"contract": astrolunaAddress,
_11
"amount": "10000000",
_11
"msg": toBase64(
_11
{
_11
"withdraw_liquidity": {}
_11
}
_11
)
_11
}
_11
}

toBase64

The code in this section is an example of a withdraw_liquidity message using feather.js to define our custom function and encode our message.


_35
const astrolunaAddress = 'terra1udsua9w6jljwxwgwsegvt6v657rg3ayfvemupnes7lrggd28s0wq7g8azm';
_35
const astrolunaLPAddress = 'terra1886vn036tc9e7ejx8pe4nkhts3gwpdfegwc4n3u77n0q76fjdthqarl8uc';
_35
_35
const toBase64 = (obj) => {
_35
return Buffer.from(JSON.stringify(obj)).toString("base64");
_35
};
_35
_35
const withdraw = new MsgExecuteContract(
_35
wallet.key.accAddress('terra'),
_35
astrolunaLPAddress,
_35
{
_35
"send": {
_35
"contract": astrolunaAddress,
_35
"amount": "10000000",
_35
"msg": toBase64(
_35
{
_35
"withdraw_liquidity": {}
_35
}
_35
)
_35
}
_35
}
_35
)
_35
_35
const fee = new Fee(2000000, { uluna: 200000 });
_35
_35
// // BROADCAST TRANSACTION
_35
const tx = await wallet.createAndSignTx({
_35
msgs: [withdraw],
_35
fee,
_35
chainID: 'pisco-1',
_35
});
_35
_35
const result = await lcd.tx.broadcast(tx, 'pisco-1');
_35
_35
console.log(result);

withdraw_liquidity

To withdraw_liquidity, you need to execute a contract message pointing to the send endpoint in the CW20 contract of the LP token associated with the pair.

send includes the contract the tokens are being sent to (pair contract address), the amount to send (withdraw), and a binary encoded msg containing our contract call (withdraw: {}).

Encoding our Msg

To encode our message, there are two common options:

The code in this section uses a custom function (toBase64) to display our binary message - this function needs to be defined elsewhere to be used. The actual string representation of our message would be an encoded binary.

toBase64

The code in this section is an example of a withdraw_liquidity message using feather.js to define our custom function and encode our message.


_11
{
_11
"send": {
_11
"contract": astrolunaAddress,
_11
"amount": "10000000",
_11
"msg": toBase64(
_11
{
_11
"withdraw_liquidity": {}
_11
}
_11
)
_11
}
_11
}