Transfer tokens using MultiversX JavaScript SDK

julian-io

Julian.io

Posted on June 22, 2024

Transfer tokens using MultiversX JavaScript SDK

In the second article and video about MultiversX JavaScript/TypeScript SDK, I want to focus on token transfers. Please check the first one, which presents all the helper tools and setup. I will also use them here. At the end of this article, you'll find the video with a voiceover and a link to the repository.

What I want to cover here:

  • How to send native EGLD using transfer methods
  • How to send ESDTs using transfer methods

The main focus will be preparing and using transaction factories, in this case, the transfer transaction factory.

How to send native EGLD using transfer methods

In the previous video, we also sent the EGLD native tokens using a custom Transaction class, but there is a simpler and cleaner method.

Let's check the code below and then analyze it step by step.

import {
  TransactionComputer,
  Address,
  TransactionsFactoryConfig,
  TransferTransactionsFactory,
} from "@multiversx/sdk-core";
import {
  receiverAddress,
  syncAndGetAccount,
  senderAddress,
  getSigner,
  apiNetworkProvider,
} from "./setup.js";

const makeTransfer = async () => {
  const user = await syncAndGetAccount();
  const computer = new TransactionComputer();
  const signer = await getSigner();

  // Prepare transfer transactions factory
  const factoryConfig = new TransactionsFactoryConfig({ chainID: "D" });
  const factory = new TransferTransactionsFactory({ config: factoryConfig });

  // Transfer native EGLD token (value transfer, the same as with the simple transaction)
  const egldTransaction = factory.createTransactionForNativeTokenTransfer({
    sender: new Address(senderAddress),
    receiver: new Address(receiverAddress),
    // 0.01 EGLD (EGLD has 18 decimal places)
    nativeAmount: BigInt("10000000000000000"),
  });

  egldTransaction.nonce = user.getNonceThenIncrement();

  const serializedEgldTransaction =
    computer.computeBytesForSigning(egldTransaction);

  egldTransaction.signature = await signer.sign(serializedEgldTransaction);

  const txHash = await apiNetworkProvider.sendTransaction(egldTransaction);

  console.log(
    "EGLD sent. Check in the Explorer: ",
    `https://devnet-explorer.multiversx.com/transactions/${txHash}`
  );
};

makeTransfer();
Enter fullscreen mode Exit fullscreen mode

So, the structure is very similar to that of the first article. What is different here is how we build our transaction object. But let's analyze the makeTransfer function starting from the top.

First, we prepare the objects required for all transactions: the user object, the transaction computer for serialization, and the signer.

const user = await syncAndGetAccount();
const computer = new TransactionComputer();
const signer = await getSigner();
Enter fullscreen mode Exit fullscreen mode

For more information, please check the first article. I won't focus on it much.

Preparing the transfer transaction factory is the central part of this process. You must prepare the config and the factory itself.

const factoryConfig = new TransactionsFactoryConfig({ chainID: "D" });
const factory = new TransferTransactionsFactory({ config: factoryConfig });
Enter fullscreen mode Exit fullscreen mode

Then, you will prepare the transaction for signing (and later broadcast) by invoking the createTransactionForNativeTokenTransfer method on the previously prepared factory.

const egldTransaction = factory.createTransactionForNativeTokenTransfer({
  sender: new Address(senderAddress),
  receiver: new Address(receiverAddress),
  // 0.01 EGLD (EGLD has 18 decimal places)
  nativeAmount: BigInt("10000000000000000"),
});
Enter fullscreen mode Exit fullscreen mode

You need to pass the receiver and sender. Also, you need to pass nativeAmount, which should be a BigInt, and remember that native EGLD has 18 decimal places. So to send 0.01 EGLD, you need to pass BigInt("10000000000000000"). Of course, there are helpers for that, or you can build one using, for example, the bignumber.js library, but let's keep it simple for now.

Ok this is all. You have prepared the transaction for signing.

The following steps are similar to the first video, the first transaction example. You need to set and increment the nonce, serialize the transaction, sign it, and send it.

egldTransaction.nonce = user.getNonceThenIncrement();

const serializedEgldTransaction =
  computer.computeBytesForSigning(egldTransaction);

egldTransaction.signature = await signer.sign(serializedEgldTransaction);

const txHash = await apiNetworkProvider.sendTransaction(egldTransaction);

console.log(
  "EGLD sent. Check in the Explorer: ",
  `https://devnet-explorer.multiversx.com/transactions/${txHash}`
);
Enter fullscreen mode Exit fullscreen mode

That's it. You broadcasted the transaction that moved the EGLD value from one wallet to another. Check the video for more details.

How to send ESDTs using transfer methods

Now, let's check how we can move ESDT tokens, which are custom tokens that you can issue on the MultiversX blockchain. They can be fungible, semi-fungible, or non-fungible (there are also meta tokens, but let's leave them; they are all similar).

As with the EGLD, let's see the whole code first, and then we will proceed step by step. It will be code from the multi-transfer demo. The video will provide a more detailed walkthrough for a single transfer of each token type, but the code is very similar to this one, so let's use it here.

import {
  TransactionComputer,
  Address,
  TransactionsFactoryConfig,
  TransferTransactionsFactory,
  TokenTransfer,
  Token,
} from "@multiversx/sdk-core";
import {
  receiverAddress,
  syncAndGetAccount,
  senderAddress,
  getSigner,
  apiNetworkProvider,
} from "./setup.js";

const makeTransfer = async () => {
  const user = await syncAndGetAccount();
  const computer = new TransactionComputer();
  const signer = await getSigner();

  // Prepare transfer transactions factory
  const factoryConfig = new TransactionsFactoryConfig({ chainID: "D" });
  const factory = new TransferTransactionsFactory({ config: factoryConfig });

  // Transfer native EGLD token (value transfer, the same as with the simple transaction)
  const multiTransferTransaction =
    factory.createTransactionForESDTTokenTransfer({
      sender: new Address(senderAddress),
      receiver: new Address(receiverAddress),
      tokenTransfers: [
        new TokenTransfer({
          token: new Token({
            identifier: "ELVNFACE-762e9d",
            nonce: BigInt("90"),
          }),
          // Send 1, it is always 1 for NFTs
          amount: BigInt("1"), // or 1n
        }),
        new TokenTransfer({
          token: new Token({ identifier: "DEMSFT-00eac9", nonce: BigInt("1") }),
          // Send 10
          amount: BigInt("10"), // or 10n
        }),
        new TokenTransfer({
          token: new Token({ identifier: "DEMFUNGI-3ec13b" }),
          // Send 10, remember about 18 decimal places
          amount: BigInt("10000000000000000000"), // or 10000000000000000000n
        }),
      ],
    });

  multiTransferTransaction.nonce = user.getNonceThenIncrement();

  const serializedmultiTransferTransaction = computer.computeBytesForSigning(
    multiTransferTransaction
  );

  multiTransferTransaction.signature = await signer.sign(
    serializedmultiTransferTransaction
  );

  const txHash = await apiNetworkProvider.sendTransaction(
    multiTransferTransaction
  );

  console.log(
    "Multiple ESDTs sent. Check in the Explorer: ",
    `https://devnet-explorer.multiversx.com/transactions/${txHash}`
  );
};

makeTransfer();
Enter fullscreen mode Exit fullscreen mode

As you can see, the code looks very similar to the native EGLD transfer, and that's the case. We want to have an excellent developer experience here. What changes here is the method on our transfer factory which is now createTransactionForESDTTokenTransfer.

const multiTransferTransaction =
  factory.createTransactionForESDTTokenTransfer({
    sender: new Address(senderAddress),
    receiver: new Address(receiverAddress),
    tokenTransfers: [
      new TokenTransfer({
        token: new Token({
          identifier: "ELVNFACE-762e9d",
          nonce: BigInt("90"),
        }),
        // Send 1, it is always 1 for NFTs
        amount: BigInt("1"), // or 1n
      }),
      new TokenTransfer({
        token: new Token({ identifier: "DEMSFT-00eac9", nonce: BigInt("1") }),
        // Send 10
        amount: BigInt("10"), // or 10n
      }),
      new TokenTransfer({
        token: new Token({ identifier: "DEMFUNGI-3ec13b" }),
        // Send 10, remember about 18 decimal places
        amount: BigInt("10000000000000000000"), // or 10000000000000000000n
      }),
    ],
  });
Enter fullscreen mode Exit fullscreen mode

We now use the tokenTransfers field instead of nativeAmount. We don't need to move any EGLD value here. In tokenTransfers, you can put different token types where you will define the identifier, amount, and in the case of NFT/SFT/Meta, also proper nonce.

Let's go through all three cases. For non-fungible ESDT, you have:

new TokenTransfer({
  token: new Token({
    identifier: "ELVNFACE-762e9d",
    nonce: BigInt("90"),
  }),
  // Send 1, it is always 1 for NFTs
  amount: BigInt("1"), // or 1n
}),
Enter fullscreen mode Exit fullscreen mode

The TokenTransfer and Token classes are, of course, imported from MultiversX SDK. The identifier here, ELVNFACE-762e9d, is, in fact, the identifier of the NFT collection. Then, for a particular NFT to send, which is here ELVNFACE-762e9d-5a, you need to pass its nonce, here 5a, which is a decimal hex representation of 90. Don't worry. You'll also find the nonce in the MultiversX Explorer. Check the video for more details. Next we need to pass the amount to send. It will always be 1 for NFTs and should be passed as BigInt.

The situation is very similar for the SFTs. The main difference is that you can pass more than 1 in the amount.

new TokenTransfer({
  token: new Token({ identifier: "DEMSFT-00eac9", nonce: BigInt("1") }),
  // Send 10
  amount: BigInt("10"), // or 10n
}),
Enter fullscreen mode Exit fullscreen mode

We send 10 of DEMSFT-00eac9-01 where 01 is nonce 1 and the collection is DEMSFT-00eac9.

There is a critical difference between the fungible ESDTs. They can have decimal places, so you need to remember this when transferring. (By the way, remember that also when transferring Meta ESDTs).

new TokenTransfer({
  token: new Token({ identifier: "DEMFUNGI-3ec13b" }),
  // Send 10, remember about 18 decimal places
  amount: BigInt("10000000000000000000"), // or 10000000000000000000n
}),
Enter fullscreen mode Exit fullscreen mode

We don't need the nonce here. There is no 'collection'. The identifier is our token identifier. And as mentioned you need to remember about the decimal places and pass the value as BigInt.

Summary

Here you have it. You learned how to transfer different types of tokens on the MultiversX blockchain using one of the most used programming language and SDK.

If you want to test with your wallets on the devnet and you need to get some tokens, check these services:

Follow me on X (@theJulianIo) and YouTube (@julian_io) or GitHub for more MultiversX magic.

Please check the tools I maintain: the Elven Family and Buildo.dev. With Buildo, you can do a lot of management operations using a nice web UI. You can issue fungible tokens, non-fungible tokens. You can also do other operations, like multi-transfers or claiming developer rewards. There is much more.

Walkthrough video

The demo code

💖 💪 🙅 🚩
julian-io
Julian.io

Posted on June 22, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related