Skip to main content

Design Swap System

The swap system facilitates easy token exchanges, allowing for the seamless swapping of CW20 tokens() for $XPLA(), as well as for other CW20 tokens!

πŸ™€ Can't wait to try out the swap feature? You can do so at Web3 Gaming Ops! In this lesson, we'll delve into the mechanics of how swapping is structured between CW20 tokens() and $XPLA(). But first, let's refresh our understanding of what a Pair is and how it forms the foundation of the swap feature.

What is Pair?
Pair is a smart contract that stores coins and tokens for swap. Let's assume that Alice requests a Swap in the Pair contract, exchanging her coin A for coin B. In this process, the Pair contract will give Alice B coin from its reserves in exchange for her A coin. This is how a Swap is carried out, and it's important to note that Pair is essential for this operation.
info

In this lesson, we are using the Dezswap Contract to create the Pair. This contract employs the Automated Market-Maker (AMM) protocol. For more detailed information, please refer to the Dezswap Docs. You are welcome to develop a new contract to create your own Pair, or you can use Dezswap as demonstrated in the example.

Table of Contents​

The Swap feature is designed in the following steps.

  1. Prepare CW20 Tokens

  2. Create Pair

  3. Increase Allowance

  4. Provide Liquidity

  5. Execute Swap

0. Prepare CW20 Tokens​

For the example, we have created a CW20 token named ACAD. The contract address for this token is xpla13mxqv7yqedcpm67fw7yz7p4s32rlsdkaae3ecmc0tram72ewefts2uu3ax.

Below is the JavaScript code used to create the CW20 token. This code is similar to the one used in Issue and Transfer Tokens (CW20) using JavaScript, with modifications only to the name and symbol:

const { LCDClient, MnemonicKey, MsgInstantiateContract } = require("@xpla/xpla.js");

const lcd = new LCDClient({
chainID: 'cube_47-5',
URL: 'https://cube-lcd.xpla.dev'
});

const main = async () => {
const mk = new MnemonicKey({
mnemonic: 'myth snow ski simple century dad gun dolphin sail lawsuit fringe image toast betray frown keep harbor flash table prevent isolate panic tag vehicle' // μ—¬λŸ¬λΆ„μ˜ λ‹ˆλͺ¨λ‹‰ λ‹¨μ–΄λ‘œ λ°”κΏ”μ£Όμ„Έμš”.
})

const wallet = lcd.wallet(mk);
const myWalletAddress = wallet.key.accAddress;

const init_msg = {
name: "CW20 Contract for Swap Tutorial",
symbol: "ACAD",
decimals: 6,
initial_balances: [{ address: myWalletAddress, amount: "2000000000000000" }],
mint: {
minter: myWalletAddress
}
};

const instantiate = new MsgInstantiateContract(
myWalletAddress,
myWalletAddress,
1,
init_msg,
{},
'Example for Swap',
);

const signedTx = await lcd.wallet(mk).createAndSignTx({
msgs: [instantiate]
});

const txResult = await lcd.tx.broadcastSync(signedTx);
console.log(txResult.txhash);
}
main()

1. Create Pair​

For the example, we will use Dezswap's dezswap_factory contract to create a Pair. Factory contract performs the function of managing token Pair. The contract address for dezswap_factory is xpla1j33xdql0h4kpgj2mhggy4vutw655u90z7nyj4afhxgj4v5urtadq44e3vd on the Mainnet and xpla1j4kgjl6h4rt96uddtzdxdu39h0mhn4vrtydufdrk4uxxnrpsnw2qug2yx2 on the Testnet.

The JavaScript code to Execute the create_pair function of the dezswap_factory contract is as follows:

const { LCDClient, MnemonicKey, MsgExecuteContract } = require("@xpla/xpla.js");

const lcd = new LCDClient({
chainID: 'cube_47-5',
URL: 'https://cube-lcd.xpla.dev'
});

const main = async () => {
const mk = new MnemonicKey({
mnemonic: 'myth snow ski simple century dad gun dolphin sail lawsuit fringe image toast betray frown keep harbor flash table prevent isolate panic tag vehicle'
})

const wallet = lcd.wallet(mk);
const myWalletAddress = wallet.key.accAddress;

const testnet_dezswap_factory = "xpla1j4kgjl6h4rt96uddtzdxdu39h0mhn4vrtydufdrk4uxxnrpsnw2qug2yx2";

const createPairMsg = new MsgExecuteContract(
myWalletAddress,
testnet_dezswap_factory,
{
"create_pair": {
"assets": [
{
"info": {
"native_token": {
"denom": "axpla"
}
},
"amount": "0"
},
{
"info": {
"token": {
"contract_addr": "xpla13mxqv7yqedcpm67fw7yz7p4s32rlsdkaae3ecmc0tram72ewefts2uu3ax" // Your Contract Address
}
},
"amount": "0"
}
]
}
}
);

const signedTx = await lcd.wallet(mk).createAndSignTx({
msgs: [createPairMsg]
});

const txResult = await lcd.tx.broadcastSync(signedTx);
console.log(txResult.txhash);
}
main()

After executing the above code, we created a Pair on the Testnet! The transaction can be found on the XPLA Explorer. When you executing this code, remember to fill in the contract_addr field with the address of the CW20 token contract you created.

Upon reviewing the Event Logs, you can see that new contracts have been instantiated. Among these, the contract with code_id 369 is the dezswap_pair Contract, and the one with code_id 110 is the dezswap_token Contract (note that these code_ids are based on Testnet, and the code_ids on Mainnet will be different). In this example, the dezswap_pair Contract is at xpla1hzmcz38u28g6duy4gmur705e8qrdq7uvzsue4d8hdvclxz79uppsw3hfns, and the dezswap_token Contract is at xpla1ntr800lgjg59d79tr9c78kxjv9v9wm04hjdyq945663juf23hvqq0xy057.

How can I go about creating a Pair for exchanging between CW20 tokens?

In the code mentioned earlier, you can define the createPairMsg variable as follows:

const createPairMsg = new MsgExecuteContract(
myWalletAddress,
testnet_dezswap_factory,
{
"create_pair": {
"assets": [
{
"info": {
"token": {
"contract_addr": "xpla13mxqv7yqedcpm67fw7yz7p4s32rlsdkaae3ecmc0tram72ewefts2uu3ax" // Your Contract Address1
}
},
"amount": "0"
},
{
"info": {
"token": {
"contract_addr": "xpla13mxqv7yqedcpm67fw7yz7p4s32rlsdkaae3ecmc0tram72ewefts2uu3ax" // Your Contract Address2
}
},
"amount": "0"
}
]
}
}
);

2. Increase Allowance​

First, you need to supply CW20 tokens and $XPLA to the dezswap_pair Contract in the 3. Provide Liquidity step. Here, the dezswap_pair Contract executes the TransferFrom function of the CW20 token to retrieve the CW20 tokens from the wallet providing them. To ensure a smooth liquidity provision, you must allow the dezswap_pair contract to take the CW20 tokens. Therefore, we will execute the IncreaseAllowance function of the CW20 token Contract.

const { LCDClient, MnemonicKey, MsgExecuteContract } = require("@xpla/xpla.js");

const lcd = new LCDClient({
chainID: 'cube_47-5',
URL: 'https://cube-lcd.xpla.dev'
});

const main = async () => {
const mk = new MnemonicKey({
mnemonic: 'myth snow ski simple century dad gun dolphin sail lawsuit fringe image toast betray frown keep harbor flash table prevent isolate panic tag vehicle'
})

const wallet = lcd.wallet(mk);
const myWalletAddress = wallet.key.accAddress;

const cw20_contract_address = "xpla13mxqv7yqedcpm67fw7yz7p4s32rlsdkaae3ecmc0tram72ewefts2uu3ax"; // Your CW20 Contract Address

const increaseAllowanceMsg = new MsgExecuteContract(
myWalletAddress,
cw20_contract_address,
{
"increase_allowance": {
"amount": "10000000000",
"spender": "xpla1hzmcz38u28g6duy4gmur705e8qrdq7uvzsue4d8hdvclxz79uppsw3hfns" // dezswap_pair Contract Address
}
}
);

const signedTx = await lcd.wallet(mk).createAndSignTx({
msgs: [increaseAllowanceMsg]
});

const txResult = await lcd.tx.broadcastSync(signedTx);
console.log(txResult.txhash);
}
main()

The result of the above can be checked on the XPLA Explorer. When you run it, you need to enter the address of your CW20 token contract into the cw20_contract_address variable and the address of the dezswap_pair contract you created into the spender field. In the example, we set the amount value to 10000000000. Since the decimals value of the ACAD CW20 token is 6, this allows for the provision of up to 10,000 ACAD tokens.

3. Provide Liquidity​

To provide liquidity to the Pair created in step 1, we will execute the provide_liquidity function of the dezswap_pair Contract.

const { LCDClient, MnemonicKey, MsgExecuteContract, Coin } = require("@xpla/xpla.js");

const lcd = new LCDClient({
chainID: 'cube_47-5',
URL: 'https://cube-lcd.xpla.dev'
});

const main = async () => {
const mk = new MnemonicKey({
mnemonic: 'myth snow ski simple century dad gun dolphin sail lawsuit fringe image toast betray frown keep harbor flash table prevent isolate panic tag vehicle'
})

const wallet = lcd.wallet(mk);
const myWalletAddress = wallet.key.accAddress;

const dezswap_pair_contract_address = "xpla1hzmcz38u28g6duy4gmur705e8qrdq7uvzsue4d8hdvclxz79uppsw3hfns"; // dezswap_pair Contract Address

const provideLiquidityMsg = new MsgExecuteContract(
myWalletAddress,
dezswap_pair_contract_address,
{
"provide_liquidity": {
"assets": [
{
"info": {
"native_token": {
"denom": "axpla"
}
},
"amount": "10000000000000000000"
},
{
"info": {
"token": {
"contract_addr": "xpla13mxqv7yqedcpm67fw7yz7p4s32rlsdkaae3ecmc0tram72ewefts2uu3ax" // Your Contract Address
}
},
"amount": "10000000000"
}
]
}
},
[new Coin(
"axpla",
"10000000000000000000"
)],
);

const signedTx = await lcd.wallet(mk).createAndSignTx({
msgs: [provideLiquidityMsg],
});

const txResult = await lcd.tx.broadcastSync(signedTx);
console.log(txResult.txhash);
}
main()

You can check the outcome of running this code on the XPLA Explorer. For both the XPLA and CW20 token, we specified the quantities we wanted to supply in the amount field. As a result, we provided a total of 10 XPLA and 10,000 ACAD.

When creating the provideLiquidityMsg variable, it's important to note a key difference from previous steps: you need to input the axpla value as the last parameter. This enables the simultaneous supply of XPLA to the dezswap_pair Contract at the time of transaction execution. As for the CW20 token provision, it was retrieved by the dezswap_pair Contract, as explored in the 2. Increase allowance stage.

4. Execute Swap​

Now let's proceed with a Swap. In this example, we will exchange 1 ACAD token for XPLA. The format for the Contract Execute message was referenced from the dezswap GitHub.

const { LCDClient, MnemonicKey, MsgExecuteContract } = require("@xpla/xpla.js");

const lcd = new LCDClient({
chainID: 'cube_47-5',
URL: 'https://cube-lcd.xpla.dev'
});

const main = async () => {
const mk = new MnemonicKey({
mnemonic: 'myth snow ski simple century dad gun dolphin sail lawsuit fringe image toast betray frown keep harbor flash table prevent isolate panic tag vehicle'
})

const wallet = lcd.wallet(mk);
const myWalletAddress = wallet.key.accAddress;

const token_contract_address = "xpla13mxqv7yqedcpm67fw7yz7p4s32rlsdkaae3ecmc0tram72ewefts2uu3ax"; // Your Contract Address

const swapMsg = new MsgExecuteContract(
myWalletAddress,
token_contract_address,
{
"send": {
"contract": "xpla1hzmcz38u28g6duy4gmur705e8qrdq7uvzsue4d8hdvclxz79uppsw3hfns", // dezswap_pair Contract Address
"amount": "1000000",
"msg": btoa(JSON.stringify({
"swap": {
// "belief_price": Option<Decimal>,
// "max_spread": Option<Decimal>,
// "to": Option<HumanAddr>
}
}))
}
}
);

const signedTx = await lcd.wallet(mk).createAndSignTx({
msgs: [swapMsg],
});

const txResult = await lcd.tx.broadcastSync(signedTx);
console.log(txResult.txhash);
}
main()

You can verify the results of running the above code on the XPLA Explorer. You need to input the CW20 contract address in the token_contract_address variable and the dezswap_pair contract address in the contract field within the msg. It's important to note that the contract to be executed differs when exchanging XPLA for CW20 tokens. For more details, please refer to the dezswap GitHub.

Now, you might wonder why, after putting in 1 ACAD token, you received about 0.0001 XPLA. This is because, during the 3. Provide Liquidity stage, a total of 10 XPLA and 10,000 ACAD were supplied to the Pair. Therefore, the product of the number of tokens in the Pair should always be maintained at 10 Γ— 10,000 = 100,000(CPMM). When 1 ACAD token is added, the product of the token numbers in the Pair should be (10 - XPLA_out) Γ— (10,000 + 1) = 100,000.

Also, with dezswap, there's a LP Commission of 0.3% given to the liquidity providerduring a swap. After accounting for this 0.3% fee, you end up receiving about 0.0001 XPLA. For a more detailed explanation, you can consult the dezswap Docs.

How do I exchange XPLA for CW20 tokens?

In the code above, you can enter the swapMsg as follows:

const swapMsg = new MsgExecuteContract(
myWalletAddress,
dezswap_pair_contract_address,
{
"swap": {
"offer_asset": {
"info": {
"native_token": {
"denom": "axpla"
}
},
"amount": "1000000000000000000"
},
//"belief_price": Option<Decimal>,
//"max_spread": Option<Decimal>,
//"to": Option<HumanAddr>
}
},
[new Coin(
"axpla",
"1000000000000000000"
)],
);

When exchanging CW20 tokens for XPLA, you executed the CW20 token contract. However, to exchange XPLA for CW20 tokens, you must execute the dezswap_pair_contract_address. Additionally, you need to include the amount of XPLA you want to swap as a contract parameter, using the new Coin("axpla", "..") format.

Conclusion​

We have explored how to develop a Swap feature using the Dezswap contract. Now, try connecting the CW20 token from your developed game with XPLA through the Swap contract.