Skip to main content

Decode Account Number and Sequence in Transactions

During the process of issuing tokens (CW20), when signing transactions, you filled in the accountNumber and sequence information as shown below.

const userSignOption = { // Signing details 
chainID: 'cube_47-5',
accountNumber: acc.account_number,
sequence: acc.sequence,
signMode: SignMode.SIGN_MODE_DIRECT
}

In this step, we'll explore what accountNumber and sequence are, and what roles they play.

Definition

Account Number

Each wallet is assigned an accountNumber when it first interacts with the XPLA blockchain. For instance, in the step where you retrieved transaction data using the address xpla1cwduqw0z8y66mnfpev2mvrzzzu98tuexepmwrk, the accountNumber 28367 indicates that it was the 28368th interaction with the XPLA blockchain on the testnet. The wallet address xpla1psv57747avcnkwfx9z6q2l396z22uqafu8gewg was the first to interact with the testnet blockchain, with an accountNumber of 0.

Simply creating a wallet does not grant you an accountNumber. You need to record your wallet address information on the XPLA blockchain by receiving token transfer transactions to acquire an accountNumber.

Sequence

Sequence refers to the sequential number of transactions generated by each wallet. If a wallet creates its first transaction, the sequence value becomes 0, and each subsequent transaction increases the sequence value by 1. It's similar to the Nonce concept in the Ethereum blockchain.

Likewise, in the step where you retrieved transaction data using the address xpla1cwduqw0z8y66mnfpev2mvrzzzu98tuexepmwrk, the transaction with hash ECDBC35B66384DEE25987AA0DDAE8CF946D1F4907B04A0E77939988474013353 has a sequence value of 0. This indicates that it was the first transaction created by that address.

Let's consider another example. A transaction with hash FBD6E043781992215A53B285403628D3D4734AF4FC0003875775AB673F2D2737 has a sequence value of 466, indicating that the address xpla1cwduqw0z8y66mnfpev2mvrzzzu98tuexepmwrk created its 467th transaction.

Due to the Sequence value, each transaction is unique. You cannot replicate a transaction and use it. Therefore, each transaction created by a specific wallet will have a distinct sequence.

For example, let's think about Alice sending 1 XPLA to Bob in a transaction. Without the Sequence item, Bob could replicate and propagate Alice's transaction, causing Alice's wallet balance to be depleted. However, this is prevented due to the Sequence value. Transactions with the same Sequence value cannot be recorded on the blockchain. If someone tries to create a transaction with the same Sequence as a transaction already recorded on the blockchain, an Account Sequence Mismatch Error occurs. This helps prevent attacks that repeatedly execute the same transaction.

Understanding through Code

Let's understand the role of Sequence through an example code. We'll look at code that sends 1 axpla to multiple wallets at once.

Preview

First, enter your mnemonic words into the code and modify as needed. If you like, you can also assign different wallet addresses to the receivers variable. After a short wait, you'll see the execution results on the right side.

const { LCDClient, MnemonicKey, MsgSend } = 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 distributor = lcd.wallet(mk);

  const distributorInfo = await lcd.auth.accountInfo(distributor.key.accAddress);
  const accountNumber = distributorInfo.account_number;
  const sequence = distributorInfo.sequence;

  const receivers = [
    "xpla13hpy8pq6799d66qlnfql6jr7vfudq8rhqt66ma",
    "xpla15msreuqde07m5070qsxvflqq3xy5tx2p4anhqu",
    "xpla1lgx8hzlpytrvz0g9s24gspzfl6zh68srj7gwu8",
    "xpla1uh30ekv9ll3e05nzl75euuy7qkjrlcreh5zmju",
    "xpla1znpv2y0mm2wce79wvpces0yakmep42nj4xhpky",
  ]

  for (let i = 0; i < receivers.length; i++) {
    try {
      const send = new MsgSend(
        distributor.key.accAddress,
        receivers[i],
        { axpla: 1 }
      );
    
      const signedTx = await lcd.wallet(mk).createAndSignTx({ // Creating and signing the transaction
        msgs: [send],
        memo: 'Air Drop Test',
        accountNumber,
        sequence
        // sequence : sequence + i
      });
    
      const result = await lcd.tx.broadcastSync(signedTx);
      console.log(result);

    } catch (error) {
      console.log(error.response.data.message);
    }
  }
}

main();


You'll notice that only the first transaction hash is correctly displayed, while the rest show errors.

Currently, the example code has the same sequence value entered for all transactions, causing errors. Modify the 39th line from sequence to sequence + i and try running it again. This time, five transaction hashes should be generated without errors.

If you've grasped the Preview code, feel free to skip the following explanation and move on to the next step.

Running the Code

  1. Create a file named "example-8.js".

  2. Copy the code below and paste it into the "example-8.js" file, then save it.

    const { LCDClient, MnemonicKey, MsgSend } = 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 distributor = lcd.wallet(mk);

    const distributorInfo = await lcd.auth.accountInfo(distributor.key.accAddress);
    const accountNumber = distributorInfo.account_number;
    const sequence = distributorInfo.sequence;

    const receivers = [
    "xpla13hpy8pq6799d66qlnfql6jr7vfudq8rhqt66ma",
    "xpla15msreuqde07m5070qsxvflqq3xy5tx2p4anhqu",
    "xpla1lgx8hzlpytrvz0g9s24gspzfl6zh68srj7gwu8",
    "xpla1uh30ekv9ll3e05nzl75euuy7qkjrlcreh5zmju",
    "xpla1znpv2y0mm2wce79wvpces0yakmep42nj4xhpky",
    ]

    for (let i = 0; i < receivers.length; i++) {
    try {
    const send = new MsgSend(
    distributor.key.accAddress,
    receivers[i],
    { axpla: 1 }
    );

    const signedTx = await lcd.wallet(mk).createAndSignTx({ // Creating and signing the transaction
    msgs: [send],
    memo: 'Air Drop Test',
    accountNumber,
    sequence
    // sequence : sequence + i
    });

    const result = await lcd.tx.broadcastSync(signedTx);
    console.log(result);

    } catch (error) {
    console.log(error.response.data.message);
    }
    }
    }

    main();
  3. Replace the mnemonic words in the 10th line with your own. You can also assign different wallet addresses to the receivers variable if desired.

  4. Enter the following command in your VSCode Terminal.

    node example-8.js
  5. Check the results in the terminal. You'll see that only the first transaction's hash is printed correctly, while the others display errors.

Explanation

First, we imported wallets using mnemonic words. You likely loaded your wallet with different mnemonic words.

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 distributor = lcd.wallet(mk);

Now, we're using the LCD API to get the accountNumber and sequence values for the wallet we're going to use to send $XPLA.

const distributorInfo = await lcd.auth.accountInfo(distributor.key.accAddress);
const accountNumber = distributorInfo.account_number;
const sequence = distributorInfo.sequence;

List the addresses of wallets that will receive $XPLA coins.

const receivers = [
"xpla13hpy8pq6799d66qlnfql6jr7vfudq8rhqt66ma",
"xpla15msreuqde07m5070qsxvflqq3xy5tx2p4anhqu",
"xpla1lgx8hzlpytrvz0g9s24gspzfl6zh68srj7gwu8",
"xpla1uh30ekv9ll3e05nzl75euuy7qkjrlcreh5zmju",
"xpla1znpv2y0mm2wce79wvpces0yakmep42nj4xhpky",
]

This code creates and broadcasts transactions to transfer 1 axpla to each wallet.

for (let i = 0; i < receivers.length; i++) {
try {
const send = new MsgSend(
distributor.key.accAddress,
receivers[i],
{ axpla: 1 }
);

const signedTx = await lcd.wallet(mk).createAndSignTx({ // Creating and signing the transaction
msgs: [send],
memo: 'Air Drop Test',
accountNumber,
sequence
// sequence : sequence + i
});

const result = await lcd.tx.broadcastSync(signedTx);
console.log(result);

} catch (error) {
console.log(error.response.data.message);
}
}

The part causing errors here is on the 39th line, specifically the sequence part. Following the code as is, all transactions would end up having the same sequence value whenever they're generated. This means only the first transaction would work correctly, and subsequent transactions would have the same sequence value as the first one, causing them not to be recorded on the blockchain.

If you want to generate all transactions without errors, instead of using the sequence code, try inputting sequence: sequence + i code and see how it goes when you run it. By doing this, if you make sure each transaction has a different sequence value, the transactions will be generated successfully.

Did all the transaction hash values come out fine? Use the XPLA Explorer to check the transaction data and see.

Wrapping Up

This is how Account Number and Sequence work. Now, let's proceed to the next step and delve deeper into Web3 functionality.