Skip to main content

ESDT Transfers

Transfer fungible ESDT tokens between addresses.

What is ESDT?

ESDT (eStandard Digital Token) is MultiversX's native token standard. Unlike EGLD transfers, ESDT transfers use encoded data in the transaction.

Simple ESDT Transfer

import 'package:abidock_mvx/abidock_mvx.dart';
import 'dart:convert';
import 'dart:typed_data';

void main() async {
final provider = GatewayNetworkProvider.devnet();
final account = await Account.fromMnemonic('your mnemonic...');

// Get account info
final config = await provider.getNetworkConfig();
final accountInfo = await provider.getAccount(account.address);

// Token details
final tokenIdentifier = 'USDC-c76f1f';
final amount = BigInt.from(1000000); // 1 USDC (6 decimals)
final recipient = Address.fromBech32('erd1...recipient...');

// Build ESDT transfer data
final data = 'ESDTTransfer@${_hexEncode(tokenIdentifier)}@${amount.toRadixString(16)}';

// Create transaction
final tx = Transaction(
sender: account.address,
receiver: recipient,
value: Balance.zero(),
nonce: accountInfo.nonce,
gasLimit: GasLimit(500000),
gasPrice: GasPrice(1000000000),
chainId: ChainId(config.chainId),
version: TransactionVersion(1),
data: Uint8List.fromList(utf8.encode(data)),
);

final signature = await account.signTransaction(tx);
final signed = tx.copyWith(newSignature: Signature.fromUint8List(signature));
final hash = await provider.sendTransaction(signed);

print('Sent: $hash');
}

String _hexEncode(String str) {
return str.codeUnits.map((c) => c.toRadixString(16).padLeft(2, '0')).join();
}

Token Decimals

Different tokens have different decimal places:

TokenDecimals1 Unit
USDC61000000
WEGLD181000000000000000000
MEX181000000000000000000
// Helper function
BigInt tokenAmount(double amount, int decimals) {
return BigInt.from(amount * pow(10, decimals));
}

// Usage
final oneUsdc = tokenAmount(1.0, 6); // 1000000
final oneWegld = tokenAmount(1.0, 18); // 1000000000000000000

Check Token Balance

void main() async {
final provider = GatewayNetworkProvider.devnet();
final address = Address.fromBech32('erd1...');

// Get all ESDT tokens
final tokens = await provider.getFungibleTokensOfAccount(address);

for (final token in tokens) {
print('${token.identifier}: ${token.balance}');
}

// Get specific token
final usdcBalance = await provider.getTokenOfAccount(
address,
'USDC-c76f1f',
);
print('USDC Balance: ${usdcBalance.balance}');
}

Transfer to Smart Contract

When sending ESDT to a contract with a function call, use SmartContractController:

void main() async {
final provider = GatewayNetworkProvider.devnet();
final account = await Account.fromMnemonic('your mnemonic...');

final config = await provider.getNetworkConfig();
final networkAccount = await provider.getAccount(account.address);

// Load contract ABI
final abi = SmartContractAbi.fromJson(abiJson);
final contractAddress = SmartContractAddress.fromBech32('erd1qqq...');

final controller = SmartContractController(
contractAddress: contractAddress,
abi: abi,
networkProvider: provider,
);

// Create ESDT transfer + contract call
final tx = await controller.call(
account: account,
nonce: networkAccount.nonce,
endpointName: 'deposit',
arguments: [], // Function arguments if any (native Dart types)
tokenTransfers: [
TokenTransferValue.fromPrimitives(
tokenIdentifier: 'WEGLD-bd4d79',
amount: BigInt.parse('500000000000000000'), // 0.5 WEGLD
),
],
options: BaseControllerInput(gasLimit: GasLimit(15000000)),
);

final hash = await provider.sendTransaction(tx);
print('Deposit: $hash');
}

For simple transfers without contract calls, use TransfersController:

final transfersController = TransfersController(
chainId: ChainId(config.chainId),
);

final tx = await transfersController.createTransactionForTokenTransfer(
account,
networkAccount.nonce,
TokenTransferInput(
receiver: recipient,
transfers: [
TokenTransfer.fungible(
tokenIdentifier: 'WEGLD-bd4d79',
amount: BigInt.parse('500000000000000000'),
),
],
),
);

Complete Example

import 'package:abidock_mvx/abidock_mvx.dart';
import 'dart:math';

void main() async {
print('=== ESDT Transfer Demo ===\n');

final provider = GatewayNetworkProvider.devnet();
final account = await Account.fromMnemonic('your mnemonic...');
final signer = UserSigner(account.secretKey);

print('Sender: ${account.address.bech32}');

// Get account info
final config = await provider.getNetworkConfig();
final networkAccount = await provider.getAccount(account.address);

// Check token balance
final tokenId = 'WEGLD-bd4d79';

try {
final tokenBalance = await provider.getTokenOfAccount(
account.address,
tokenId,
);
final balanceValue = BigInt.parse(tokenBalance.balance);
print('Balance: ${balanceValue.toDouble() / 1e18} WEGLD');
} on NetworkException catch (e) {
if (e.statusCode == 404) {
print('Token not found or zero balance');
} else {
print('Network error: ${e.message}');
}
return;
}

// Transfer details
final recipient = Address.fromBech32(
'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx'
);
final amount = BigInt.parse('10000000000000000'); // 0.01 WEGLD

// Build ESDT transfer using TransferTransactionsFactory
final factory = TransferTransactionsFactory(
config: TransferTransactionsConfig(chainId: ChainId(config.chainId)),
);

final tx = factory.createTransactionForEsdtTransfer(
sender: account.address,
receiver: recipient,
tokenTransfers: [
TokenTransfer.fungible(
tokenIdentifier: tokenId,
amount: amount,
),
],
);

print(' Token: $tokenId');
print(' Amount: ${amount.toDouble() / 1e18} WEGLD');
print(' To: ${recipient.bech32.substring(0, 20)}...');

// Set nonce and sign transaction
final txWithNonce = tx.copyWith(newNonce: networkAccount.nonce);
final signature = await signer.sign(txWithNonce.serializeForSigning());
final signed = txWithNonce.copyWith(newSignature: Signature.fromUint8List(signature));

// Send
final hash = await provider.sendTransaction(signed);

// Wait for confirmation
final watcher = TransactionWatcher(networkProvider: provider);
final result = await watcher.awaitCompleted(hash);

}

Gas Estimation

ESDT transfers require more gas than EGLD:

OperationBase GasData Gas
Simple ESDT transfer500,000+ data bytes
ESDT to contract500,000 + function cost+ data bytes
Multi-ESDT1,000,000+ per token
int estimateEsdtGas({
required String tokenId,
required BigInt amount,
String? functionName,
List<String>? arguments,
}) {
var gas = 500000;

if (functionName != null) {
gas += 5000000; // Base contract call
gas += (arguments?.length ?? 0) * 50000; // Per argument
}

return gas;
}

Next Steps