Issue and send CW20 Tokens (Javascript)
Using CW20 makes it easier to connect a gaming ecosystem with the XPLA blockchain. This time, let's write code directly to issue tokens.
But I have zero idea what CW20 is!
The XPLA Chain utilizes a smart contract platform called Cosmwasm
. Just like ERC20 on Ethereum, CW20 is the standard for Fungible tokens in Cosmwasm
. For more, refer to the docs for Cosmwasm.
Preview
First, here's the code to create a CW20 contract using JavaScript. Modify and run the code with your mnemonic phrase. You'll see the execution result on the right side after a short wait.
Have you successfully created the contract? Great, then the CW20 has been issued! Now, let's try sending the issued CW20 tokens to another wallet address.
Similarly, please insert your mnemonic phrase into the code below. Afterwards, assign the address of the previously created contract to the variable cw20_contract
. After a short wait, you'll be able to see the execution result on the right side.
Where can you find the address of the created contract?
The easiest way is to use the XPLA Explorer. Take the transaction hash value from the execution result and search it on the XPLA Explorer. Then, on the Transaction Details page, go to the Event Logs
tab to find the address of the created contract. You were able to find the example contract address here.
If you’ve understood the Preview Code, you can proceed to the next step without reading the following content!
Issuing CW20 Tokens using JavaScript
Let's create a CW20 contract and issue CW20 tokens. The Code ID for the CW20 contract is 1
on both the mainnet and the testnet.
Code ID?
Unlike EVM, the Code Deploy and Contract Creation do not go hand in hand in Cosmwasm. First, when you put your code onto the blockchain (StoreCode), you'll get a special number called a Code ID
for that code. Afterward, you can use this Code ID
to make a contract (InstantiateContract).
All the contracts made with the same Code ID are based on the same original code. The only difference is that if you provide different starting values (init_msg) when making the contract, the specific details of the contract will be different. For more detailed information, you can take a look at the XPLA Docs.
Running the Code
Please create a file named "example-4.js".
Copy and paste the code below into the "example-4.js" file and save it.
const { LCDClient, MnemonicKey, MsgInstantiateContract, Fee, SignMode } = 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' // Replace with your mnemonic words
})
const wallet = lcd.wallet(mk);
const myWalletAddress = wallet.key.accAddress;
const init_msg = {
name: "My CW20 Token", // My Cw20 Token
symbol: "MCT", // My Cw20 Token
decimals: 6,
initial_balances: [{ address: myWalletAddress, amount: "2000000000000000" }], // List the addresses that will initially hold the supply as an array.
mint: { // You can also optionally specify a minter in case additional minting is needed.
minter: myWalletAddress
}
};
const instantiate = new MsgInstantiateContract(
myWalletAddress, // sender
myWalletAddress, // admin
1, // MAINNET, TESTNET CW20 code id
init_msg,
{}, // XPLA token amount to send to the contract (leave empty for now, not needed at the moment).
'My CW20 Token', // Enter the label you want to write.
);
const signedTx = await lcd.wallet(mk).createAndSignTx({ // Creating and signing the transaction
msgs: [instantiate]
});
// You can replace the createAndSignTx function with the code below
// const accInfo = await lcd.auth.accountInfo(myWalletAddress) // Getting wallet information
// const acc = accInfo;
// const userSignOption = { // Signing details
// chainID: 'cube_47-5',
// accountNumber: acc.account_number,
// sequence: acc.sequence,
// signMode: SignMode.SIGN_MODE_DIRECT
// }
// const fee = new Fee(3000000, "2550000000000000000axpla") // Setting the fee amount
// const tx = await lcd.tx.create([], { msgs: [instantiate], fee }) // Creating the transaction
// const signedTx = await wallet.key.signTx(tx, userSignOption) // Signing
const txResult = await lcd.tx.broadcastSync(signedTx);
console.log(txResult.txhash);
}
main()Please replace the mnemonic words in the 10th line with your own mnemonic phrase.
Enter the following command in the VSCode Terminal:
node example-4.js
In the Terminal, you will be able to see the results. The CW20 contract will be created, and you should be able to verify the transaction hash of the creation transaction.
Explanation
We haven't written the contract code for CW20. The code has already been deployed on the XPLA blockchain, and we've simply created the contract using the deployed code. We'll explore how to write contract code in later steps.
First, we loaded a wallet using mnemonic words. You've probably used different mnemonic words to load your own wallet.
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;
To create a CW20 contract, we've pre-set some initial values. Feel free to modify the values below and create your own token.
const init_msg = {
name: "My CW20 Token", // My Cw20 Token
symbol: "MCT", // My Cw20 Token
decimals: 6,
initial_balances: [{ address: myWalletAddress, amount: "2000000000000000" }], // List the addresses that will initially hold the supply as an array.
mint: { // You can also optionally specify a minter in case additional minting is needed.
minter: myWalletAddress
}
};
You can freely set the decimals
value when issuing tokens. In the example initial values, we've set the decimals
field to 6. Just to let you know, in the case of $XPLA, the decimals
value is 18. This means that 1018 aXPLA is equal to 1 XPLA.
Who is creating the contract (sender), and who is the owner of the contract (admin)? You prepare for contract creation by providing the Code ID, initial values, label, and other details.
const instantiate = new MsgInstantiateContract(
myWalletAddress, // sender
myWalletAddress, // admin
1, // MAINNET, TESTNET CW20 code id
init_msg,
{}, // XPLA token amount to send to the contract (leave empty for now, not needed at the moment).
'My CW20 Token', // Enter the label you want to write.
);
You generate and complete the signature for a transaction using the createAndSignTx
function.
const signedTx = await lcd.wallet(mk).createAndSignTx({ // Creating and signing the transaction
msgs: [instantiate]
});
The createAndSignTx
function generates a transaction, automatically sets the fee amount, and proceeds with the signing process. You can also separate the roles of the createAndSignTx
function into creating a transaction, setting the fee, and signing individually, as shown in the commented sections of the example code.
const accInfo = await lcd.auth.accountInfo(myWalletAddress) // Getting wallet information
const acc = accInfo;
const userSignOption = { // Signing details
chainID: 'cube_47-5',
accountNumber: acc.account_number,
sequence: acc.sequence,
signMode: SignMode.SIGN_MODE_DIRECT
}
const fee = new Fee(3000000, "2550000000000000000axpla") // Setting the fee amount
const tx = await lcd.tx.create([], { msgs: [instantiate], fee }) // Creating the transaction
const signedTx = await wallet.key.signTx(tx, userSignOption) // Signing
You transmit the created transaction to the network using the broadcastSync
function. If the transaction is successfully created on the XPLA blockchain, you'll be able to confirm the transaction hash value as the result.
const txResult = await lcd.tx.broadcastSync(signedTx);
console.log(txResult.txhash);
Check the information of the created transaction using the following code.
Create a file named "query-tx.js."
Copy and paste the code below. Instead of
ECDBC35B66384DEE25987AA0DDAE8CF946D1F4907B04A0E77939988474013353
, please insert the transaction hash value you generated earlier.
const { LCDClient } = require("@xpla/xpla.js");
const lcd = new LCDClient({
chainID: 'cube_47-5',
URL: 'https://cube-lcd.xpla.dev'
});
async function main() {
const txInfo = await lcd.tx.txInfo("ECDBC35B66384DEE25987AA0DDAE8CF946D1F4907B04A0E77939988474013353"); // Transaction data
console.log(JSON.stringify(txInfo, null, 2));
}
main()
- Execute the following command in the Terminal.
node query-tx.js
- Confirm the transaction information.
Alternatively, using the XPLA Explorer to check transaction data is also a good option.
So far, we've issued CW20 tokens. Using these tokens can enrich the gaming ecosystem even further. Now, it's time to distribute tokens to users. To allow users to acquire tokens, let's explore CW20 token transfers.
Send Issued CW20
Let's go ahead and transfer the CW20 tokens that we issued earlier. Token transfers are carried out by executing the transfer
function of the CW20 contract. For this example, we'll use the contract address of the MCT token as xpla1xljvdrtyn86kv7hrdhae4qxdy8krajah3w7xhtyrt0n69und9xdqdhrasc
, and we'll transfer the tokens to the address xpla1cwduqw0z8y66mnfpev2mvrzzzu98tuexepmwrk
.
Running the Code
Please create a file named example-5.js.
Copy the following code and paste it into the example-5.js file, then save it.
const { LCDClient, MnemonicKey, MsgExecuteContract, TxAPI, Fee } = 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 = "xpla1xljvdrtyn86kv7hrdhae4qxdy8krajah3w7xhtyrt0n69und9xdqdhrasc"; // Replace it with the address of the CW20 token created in example-4.js.
const transferMsg = new MsgExecuteContract(
myWalletAddress,
cw20_contract,
{
transfer: {
recipient : "xpla1cwduqw0z8y66mnfpev2mvrzzzu98tuexepmwrk",
amount: "100000"
}
}
);
const signedTx = await lcd.wallet(mk).createAndSignTx({ // Creating and signing the transaction
msgs: [transferMsg]
});
// const accInfo = await lcd.auth.accountInfo(myWalletAddress)
// const acc = accInfo;
// // Estimating the expected transaction fee for the message.
// const tx_api = new TxAPI(lcd)
// const simul_fee = await tx_api.estimateFee(
// [{
// sequenceNumber: acc.sequence,
// publicKey: mk.publicKey
// }],
// {
// msgs: [transferMsg],
// gasAdjustment: 1.25,
// }
// )
// const fee = new Fee(simul_fee.gas_limit, simul_fee.amount.toString());
// const signedTx = await wallet.createAndSignTx({
// msgs: [transferMsg], fee
// });
const txResult = await lcd.tx.broadcastSync(signedTx);
console.log(txResult.txhash);
}
main()Please replace the mnemonic words on the 10th line with your own mnemonic words.
Assign your contract address, which you generated during the steps to issue CW20 tokens in JavaScript, to the variable
cw20_contract
on the 16th line. In the example, replacexpla1xljvdrtyn86kv7hrdhae4qxdy8krajah3w7xhtyrt0n69und9xdqdhrasc
with your actual contract address.Where can I find the address of the contract I created?
The easiest way is to use the XPLA Explorer. Let's try searching for the transaction hash that appeared as a result of the execution on the XPLA Explorer. Once you're on the "Transaction Details" screen, go to the
Event Logs
tab. There, you'll be able to see the address of the contract that was created. You could find the example contract address right there.Finally, please write the wallet address where you will receive the CW20 tokens in the
recipient
field on the 23rd line. Instead of the example addressxpla1cwduqw0z8y66mnfpev2mvrzzzu98tuexepmwrk
, enter your own wallet address where you'll be receiving the CW20 tokens.tipDon't have a wallet to receive the tokens? You can create an additional wallet using Vault and use that address, or alternatively, you can use the same sending wallet address as the receiving address. You can send tokens to yourself.
Enter the following command in VSCode Terminal
node example-5.js
Check the results in the Terminal. You'll see that the CW20 tokens have been sent, and you'll be able to verify the transaction's hash value.
Explanation
Sending CW20 tokens works differently from sending $XPLA coins. While $XPLA coin transfers use the MsgSend
method, CW20 token transfers are done by executing the transfer
function of the CW20 contract. This is why we use the MsgExecuteContract
method.
First, you've loaded the wallet with your mnemonic words. You might have used different mnemonic words to load your wallet.
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;
Then, we've specified the contract address of the token. You would have also assigned the contract address of the token you created.
Next, we've indicated who the transfer is going to and how much CW20 you're sending. In the amount
field, you specify the quantity in uCW20 units. Remember that when you created the contract, the decimal value
was set to 6. So, if you have 100,000 uCW20, you'll be sending 0.1 CW20.
const cw20_contract = "xpla1xljvdrtyn86kv7hrdhae4qxdy8krajah3w7xhtyrt0n69und9xdqdhrasc"; // Replace it with the address of the CW20 token created in example-4.js.
const transferMsg = new MsgExecuteContract(
myWalletAddress,
cw20_contract,
{
transfer: {
recipient : "xpla1cwduqw0z8y66mnfpev2mvrzzzu98tuexepmwrk",
amount: "100000"
}
}
);
Use the createAndSignTx
function to generate a transaction and go through the signing process as well.
const signedTx = await lcd.wallet(mk).createAndSignTx({ // Creating and signing the transaction
msgs: [transferMsg]
});
The function createAndSignTx
handles creating transactions, automatically setting the fee amount, and performing the signing process. In the steps to issue CW20 tokens using JavaScript, I also provided separate code for each of these steps.
Now, let's take a look at the code that uses the estimateFee
function to create transactions. Similarly, replace the createAndSignTx
function with the code below and give it a try:
const accInfo = await lcd.auth.accountInfo(myWalletAddress)
const acc = accInfo;
// Estimating the expected transaction fee for the message.
const tx_api = new TxAPI(lcd)
const simul_fee = await tx_api.estimateFee(
[{
sequenceNumber: acc.sequence,
publicKey: mk.publicKey
}],
{
msgs: [transferMsg],
gasAdjustment: 1.25,
}
)
const fee = new Fee(simul_fee.gas_limit, simul_fee.amount.toString());
const signedTx = await wallet.createAndSignTx({
msgs: [transferMsg], fee
});
Just as the createAndSignTx
function automatically set the fee amount, this time we've used the estimateFee
function to set the fee. All the modules used in the example are included in the @xpla/xpla.js
package.
Use the broadcastSync
function to send the created transaction to the network. If the transaction has been successfully created on the XPLA blockchain, you should be able to see the transaction hash as the result.
const txResult = await lcd.tx.broadcastSync(signedTx);
console.log(txResult.txhash);
Did you receive the transaction hash value? Use the XPLA Explorer to check the transaction data and details.
Wrapping Up
We've looked into transferring CW20 tokens like this. Now, let's connect CW20 tokens to your game with the XPLA blockchain! In the next chapter, we will retrieve data of the CW20 token we created ourselves from the blockchain.