Skip to main content

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.

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()


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.

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()


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

  1. Please create a file named "example-4.js".

  2. 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()
  3. Please replace the mnemonic words in the 10th line with your own mnemonic phrase.

  4. Enter the following command in the VSCode Terminal:

    node example-4.js
  5. 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
}
};
info

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]
});
info

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.

  1. Create a file named "query-tx.js."

  2. 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()
  1. Execute the following command in the Terminal.
node query-tx.js
  1. 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

  1. Please create a file named example-5.js.

  2. 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()
  3. 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, replace xpla1xljvdrtyn86kv7hrdhae4qxdy8krajah3w7xhtyrt0n69und9xdqdhrasc 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 address xpla1cwduqw0z8y66mnfpev2mvrzzzu98tuexepmwrk, enter your own wallet address where you'll be receiving the CW20 tokens.

    tip

    Don'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.

  4. Enter the following command in VSCode Terminal

    node example-5.js
  5. 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]
});
info

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.