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:
| Token | Decimals | 1 Unit |
|---|---|---|
| USDC | 6 | 1000000 |
| WEGLD | 18 | 1000000000000000000 |
| MEX | 18 | 1000000000000000000 |
// 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:
| Operation | Base Gas | Data Gas |
|---|---|---|
| Simple ESDT transfer | 500,000 | + data bytes |
| ESDT to contract | 500,000 + function cost | + data bytes |
| Multi-ESDT | 1,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
- NFT Transfers - Transfer NFTs
- Multi Transfers - Multiple tokens
- Transaction Tracking - Monitor status