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.
_29const astrolunaAddress = "terra1udsua9w6jljwxwgwsegvt6v657rg3ayfvemupnes7lrggd28s0wq7g8azm";_29_29const 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_29const tx = await wallet.createAndSignTx({_29 msgs: [swap],_29 feeDenoms: ['uluna'],_29 chainID: 'pisco-1',_29});_29_29const result = await lcd.tx.broadcast(tx, 'pisco-1');_29_29console.log(result);
Optional Parameters
The swap endpoint takes in the following optional parameters:
belief_price: Expected return amountmax_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_priceusing the proportion of pool balances - Querying the
simulationendpoint 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.
_3const astrolunaAddress = "terra1udsua9w6jljwxwgwsegvt6v657rg3ayfvemupnes7lrggd28s0wq7g8azm";_3const { assets } = await lcd.wasm.contractQuery(astrolunaAddress, { "pool": {} });_3const 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.
_21const astrolunaAddress = "terra1udsua9w6jljwxwgwsegvt6v657rg3ayfvemupnes7lrggd28s0wq7g8azm";_21const offeredlunaAmount = "100000"_21let beliefPrice;_21_21const simulation = await lcd.wasm.contractQuery(_21astrolunaAddress, _21{_21 "simulation": {_21 "offer_asset": {_21 "info": {_21 "native_token": {_21 "denom": "uluna"_21 }_21 },_21 "amount": offeredlunaAmount_21 }_21 }_21}_21).then(result => { beliefPrice = result.return_amount });_21_21beliefPrice = (beliefPrice / offeredlunaAmount).toFixed(18);
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 amountmax_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_priceusing the proportion of pool balances - Querying the
simulationendpoint 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:
- An online base64 encoder
- A custom function
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.
_27const toBase64 = (obj) => {_27 return Buffer.from(JSON.stringify(obj)).toString("base64");_27};_27_27const 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:
- An online base64 encoder
- A custom function
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}