Skip to main content

Swapping

Overview

The pair contract for a given pool allows anyone to perform a swap between the assets in a pair. Depending on the type of assets in the pair, a user could perform two types of swaps:

  • Native swap (Native -> Native / Native -> CW20)
  • CW20 swap (CW20 -> CW20 / CW20 -> Native)

A native swap will interact with the pair contract directly while a CW20 swap will interact with the CW20 token contract and be processed by the pair contract as a Cw20HookMsg.

Native Swap

Performs a native swap in a given pool.

Swap Endpoint

To perform a native swap, you need to execute a contract message pointing to the swap endpoint in the pair contract for a given pool.

The swap endpoint requires an offer_asset parameter which contains information about the offer asset itself and the amount to send.


_15
{
_15
"swap": {
_15
"offer_asset": {
_15
"info": {
_15
"native_token": {
_15
"denom": "uluna"
_15
}
_15
},
_15
"amount": "100000"
_15
},
_15
"belief_price": beliefPrice,
_15
"max_spread": '0.005',
_15
"to": "..."
_15
}
_15
}

Coins

For native swaps, you need to send the native_token along with the transaction message.

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


_29
const astrolunaAddress = "terra1udsua9w6jljwxwgwsegvt6v657rg3ayfvemupnes7lrggd28s0wq7g8azm";
_29
_29
const swap = new MsgExecuteContract(
_29
wallet.key.accAddress('terra'),
_29
astrolunaAddress,
_29
{
_29
"swap": {
_29
"offer_asset": {
_29
"info": {
_29
"native_token": {
_29
"denom": "uluna"
_29
}
_29
},
_29
"amount": "100000"
_29
}
_29
}
_29
},
_29
new Coins({ uluna: '100000' })
_29
);
_29
_29
const tx = await wallet.createAndSignTx({
_29
msgs: [swap],
_29
feeDenoms: ['uluna'],
_29
chainID: 'pisco-1',
_29
});
_29
_29
const result = await lcd.tx.broadcast(tx, 'pisco-1');
_29
_29
console.log(result);

Optional Parameters

The swap endpoint takes in the following optional parameters:

  • belief_price: Expected return amount
  • max_spread: The difference between the ask amount before and after the swap operation. If the swap spread exceeds the provided max limit, the swap will fail.
  • to: Address receiving tokens (if different from sender)

_15
{
_15
"swap": {
_15
"offer_asset": {
_15
"info": {
_15
"native_token": {
_15
"denom": "uluna"
_15
}
_15
},
_15
"amount": "100000"
_15
},
_15
"belief_price": beliefPrice,
_15
"max_spread": '0.005',
_15
"to": "..."
_15
}
_15
}

Belief Price + Max Spread

If belief_price is provided in combination with max_spread, the pool will check the difference between the return amount (using belief_price) and the real pool price.

The belief_price +/- the max_spread is the range of possible acceptable prices for this swap.


_15
{
_15
"swap": {
_15
"offer_asset": {
_15
"info": {
_15
"native_token": {
_15
"denom": "uluna"
_15
}
_15
},
_15
"amount": "100000"
_15
},
_15
"belief_price": beliefPrice,
_15
"max_spread": '0.005',
_15
"to": "..."
_15
}
_15
}

Calculating the belief_price

To obtain the belief_price, this tutorial uses two examples:

  • Calculating the belief_price using the proportion of pool balances
  • Querying the simulation endpoint in the pair contract for which the swap is taking place

_15
{
_15
"swap": {
_15
"offer_asset": {
_15
"info": {
_15
"native_token": {
_15
"denom": "uluna"
_15
}
_15
},
_15
"amount": "100000"
_15
},
_15
"belief_price": beliefPrice,
_15
"max_spread": '0.005',
_15
"to": "..."
_15
}
_15
}

Pool Endpoint

To calculate the belief_price using the proportion of pool balances, we first need to fetch the amount of each asset in the pool. This can be done by calling the pool endpoint in the pair contract.

The code in this section is an example of using feather.js to query the pair contract, perform calculations with our response, and store the results in a variable to use as an input for our swap message.


_3
const astrolunaAddress = "terra1udsua9w6jljwxwgwsegvt6v657rg3ayfvemupnes7lrggd28s0wq7g8azm";
_3
const { assets } = await lcd.wasm.contractQuery(astrolunaAddress, { "pool": {} });
_3
const beliefPrice = (assets[0].amount / assets[1].amount).toFixed(18);

Simulation Enpoint

Astroport's native solution for calculating the belief_price is the simulation query.

The query takes in an offer_asset which contains information about the native token and an amount to send.


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

Normalizing Values

Depending on the token decimal for the assets you are swapping, you may need to normalize the response from the simulation query before passing it into your swap message.

The code in this section is an example of using feather.js to perform the simulation query, normalize the response, and store the results for later usage.


_20
const astrolunaAddress = "terra1udsua9w6jljwxwgwsegvt6v657rg3ayfvemupnes7lrggd28s0wq7g8azm";
_20
let beliefPrice;
_20
_20
const simulation = await lcd.wasm.contractQuery(
_20
astrolunaAddress,
_20
{
_20
"simulation": {
_20
"offer_asset": {
_20
"info": {
_20
"native_token": {
_20
"denom": "uluna"
_20
}
_20
},
_20
"amount": "100000"
_20
}
_20
}
_20
}
_20
).then(result => { beliefPrice = result.return_amount });
_20
_20
beliefPrice = String(beliefPrice * 1000000);

Swap Endpoint

To perform a native swap, you need to execute a contract message pointing to the swap endpoint in the pair contract for a given pool.

The swap endpoint requires an offer_asset parameter which contains information about the offer asset itself and the amount to send.

Coins

For native swaps, you need to send the native_token along with the transaction message.

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

Optional Parameters

The swap endpoint takes in the following optional parameters:

  • belief_price: Expected return amount
  • max_spread: The difference between the ask amount before and after the swap operation. If the swap spread exceeds the provided max limit, the swap will fail.
  • to: Address receiving tokens (if different from sender)

Belief Price + Max Spread

If belief_price is provided in combination with max_spread, the pool will check the difference between the return amount (using belief_price) and the real pool price.

The belief_price +/- the max_spread is the range of possible acceptable prices for this swap.

Calculating the belief_price

To obtain the belief_price, this tutorial uses two examples:

  • Calculating the belief_price using the proportion of pool balances
  • Querying the simulation endpoint in the pair contract for which the swap is taking place

Pool Endpoint

To calculate the belief_price using the proportion of pool balances, we first need to fetch the amount of each asset in the pool. This can be done by calling the pool endpoint in the pair contract.

The code in this section is an example of using feather.js to query the pair contract, perform calculations with our response, and store the results in a variable to use as an input for our swap message.

Simulation Enpoint

Astroport's native solution for calculating the belief_price is the simulation query.

The query takes in an offer_asset which contains information about the native token and an amount to send.

Normalizing Values

Depending on the token decimal for the assets you are swapping, you may need to normalize the response from the simulation query before passing it into your swap message.

The code in this section is an example of using feather.js to perform the simulation query, normalize the response, and store the results for later usage.


_15
{
_15
"swap": {
_15
"offer_asset": {
_15
"info": {
_15
"native_token": {
_15
"denom": "uluna"
_15
}
_15
},
_15
"amount": "100000"
_15
},
_15
"belief_price": beliefPrice,
_15
"max_spread": '0.005',
_15
"to": "..."
_15
}
_15
}

CW20 Swap

Performs a CW20 swap in a given pool.

Cw20HookMsg

To perform a CW20 swap, you need to execute a contract message pointing to the send endpoint in the CW20 contract of the token you are swapping.

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


_20
{
_20
"send": {
_20
"contract": astrolunaAddress,
_20
"amount": "1000000",
_20
"msg": toBase64(
_20
{
_20
"swap": {
_20
"ask_asset_info": {
_20
"native_token": {
_20
"denom": "uluna"
_20
}
_20
},
_20
"belief_price": beliefPrice,
_20
"max_spread": "0.005",
_20
"to": "..."
_20
}
_20
}
_20
)
_20
}
_20
}

swap parameters

Our swap message takes in our ask_asset_info, belief_price, max_spread, and the address tokens are being sent to (if diffent from the sender).

If belief_price is provided in combination with max_spread, the pool will check the difference between the return amount (using belief_price) and the real pool price. The belief_price +/- the max_spread creates a range of possible acceptable prices for this swap.


_20
{
_20
"send": {
_20
"contract": astrolunaAddress,
_20
"amount": "1000000",
_20
"msg": toBase64(
_20
{
_20
"swap": {
_20
"ask_asset_info": {
_20
"native_token": {
_20
"denom": "uluna"
_20
}
_20
},
_20
"belief_price": beliefPrice,
_20
"max_spread": "0.005",
_20
"to": "..."
_20
}
_20
}
_20
)
_20
}
_20
}

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.


_20
{
_20
"send": {
_20
"contract": astrolunaAddress,
_20
"amount": "1000000",
_20
"msg": toBase64(
_20
{
_20
"swap": {
_20
"ask_asset_info": {
_20
"native_token": {
_20
"denom": "uluna"
_20
}
_20
},
_20
"belief_price": beliefPrice,
_20
"max_spread": "0.005",
_20
"to": "..."
_20
}
_20
}
_20
)
_20
}
_20
}

toBase64 Encoder

The code in this section is an example of what a send message may look like feather.js to define our custom function and encode our message.


_27
const toBase64 = (obj) => {
_27
return Buffer.from(JSON.stringify(obj)).toString("base64");
_27
};
_27
_27
const cw20Swap = new MsgExecuteContract(
_27
wallet.key.accAddress('terra'),
_27
astroAddress,
_27
{
_27
"send": {
_27
"contract": astrolunaAddress,
_27
"amount": "1000000",
_27
"msg": toBase64(
_27
{
_27
"swap": {
_27
"ask_asset_info": {
_27
"native_token": {
_27
"denom": "uluna"
_27
}
_27
},
_27
"belief_price": beliefPrice,
_27
"max_spread": "0.005"
_27
}
_27
}
_27
)
_27
}
_27
}
_27
)

Calculating the belief_price

Lastly, calculating the belief_price for CW20 swaps is similar to doing so for native swaps. To learn more about this topic, refer to the section above.

The exception is the info parameter in our simulation query which is derived from an AssetInfo enum. For CW20 swaps, instead of using native_token and denom, use token and contract_addr .


_12
{
_12
"simulation": {
_12
"offer_asset": {
_12
"info": {
_12
"token": {
_12
"contract_addr": astrolunaAddress
_12
}
_12
},
_12
"amount": "1000000"
_12
}
_12
}
_12
}

Cw20HookMsg

To perform a CW20 swap, you need to execute a contract message pointing to the send endpoint in the CW20 contract of the token you are swapping.

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

swap parameters

Our swap message takes in our ask_asset_info, belief_price, max_spread, and the address tokens are being sent to (if diffent from the sender).

If belief_price is provided in combination with max_spread, the pool will check the difference between the return amount (using belief_price) and the real pool price. The belief_price +/- the max_spread creates a range of possible acceptable prices for this swap.

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 Encoder

The code in this section is an example of what a send message may look like feather.js to define our custom function and encode our message.

Calculating the belief_price

Lastly, calculating the belief_price for CW20 swaps is similar to doing so for native swaps. To learn more about this topic, refer to the section above.

The exception is the info parameter in our simulation query which is derived from an AssetInfo enum. For CW20 swaps, instead of using native_token and denom, use token and contract_addr .


_20
{
_20
"send": {
_20
"contract": astrolunaAddress,
_20
"amount": "1000000",
_20
"msg": toBase64(
_20
{
_20
"swap": {
_20
"ask_asset_info": {
_20
"native_token": {
_20
"denom": "uluna"
_20
}
_20
},
_20
"belief_price": beliefPrice,
_20
"max_spread": "0.005",
_20
"to": "..."
_20
}
_20
}
_20
)
_20
}
_20
}