This series of articles will show you how to send and receive Hedera Tokens using Smart Contracts. In this first article, you will learn how to send a fungible token from an account to a contract using the JS SDK.
This example shows just one scenario. If you’d like, you can check the other scenarios on GitHub directly.
Let’s start by creating some helper functions in utils.js. You need to make an accountCreator function that will be responsible for account creation by providing a private key, an initial balance, and your client. You must also define a tokenCreator function that deals with fungible token creation. Keep in mind that you can change parameters if you want to.
If you are not familiar with token creation, I suggest you check out this previous article.
// Creates a new account
async function accountCreator(privateKey, initialBalance, client) {
const response = await new AccountCreateTransaction()
.setInitialBalance(new Hbar(initialBalance))
.setKey(privateKey.publicKey)
.execute(client);
const receipt = await response.getReceipt(client);
return receipt.accountId;
}
// Creates a new Fungible Token (change parameters if needed)
async function tokenCreator(treasuryId, treasuryKey, client) {
//Create the transaction and freeze for manual signing
const createToken = await new TokenCreateTransaction()
.setTokenName("USD Bar") // Name
.setTokenSymbol("USDB") // Symbol
.setTreasuryAccountId(treasuryId) // Treasury account
.setInitialSupply(10000) // Initial supply
.setSupplyKey(treasuryKey) // Supply key
.setDecimals(2) // Decimals
.setMaxTransactionFee(new Hbar(20)) // Change the default max transaction fee
.freezeWith(client);
// Sign with treasuryKey
const createTokenTx = await createToken.sign(treasuryKey);
const createTokenRx = await createTokenTx.execute(client);
const createTokenReceipt = await createTokenRx.getReceipt(client);
const tokenId = createTokenReceipt.tokenId;
return tokenId;
}
2. Define Hedera Accounts
Now you can use the previously created functions to initialize in your index.js file a token sender account (Alice), an admin account for your contract, and a fungible token.
// Create admin key for contract
const adminKey = PrivateKey.generateED25519();
const adminId = await accountCreator(adminKey, 10, client);
// Alice is the sender
const aliceKey = PrivateKey.generateED25519();
const aliceId = await accountCreator(aliceKey, 10, client);
// Create token with Alice as treasury
const tokenId = await tokenCreator(aliceId, aliceKey, client);
console.log("The new token ID is " + tokenId);
Console Output:
3. Create and Store your Contract on the Hedera Network
Your Hedera Token receiver will be an empty contract called TokenReceiver.sol because you won’t need any particular functionality.
You can now compile this contract to get bytecode either using Remix IDE or solc. You can also find this contract's bytecode in this Codesandbox.
Next, you have to create your contract using ContractCreateFlow() and set an admin key. (Keep hold of this key, as you'll need it in the following steps!). For now, you need to switch operators (for the client) before executing the transaction, as you need to sign using the contract's admin key.
// Load bytecode
const bytecode = fs.readFileSync('TokenReceiver_sol_TokenReceiver.bin');
// Switch operator to sign transaction
client.setOperator(adminId, adminKey);
// Create contract with adminKey
const createContract = new ContractCreateFlow()
.setGas(100000)
.setBytecode(bytecode)
.setAdminKey(adminKey)
const createSubmit = await createContract.execute(client);
const createRx = await createSubmit.getReceipt(client);
const contractId = createRx.contractId;
console.log("The new contract ID is " + contractId);
// Switch operator back to operator account
client.setOperator(operatorId, operatorKey);
Console Output:
4. Associate Token and Remove the Admin Key
So now that your contract is on the Hedera Network, you can use the admin key to associate your token to your contract, so the token can be received.
It's time to remove the contract's admin key, as a keyless contract ensures a trustless workflow. You can use a ContractUpdateTransaction() and specify an empty KeyList.
Now you can transfer the fungible token to your contract using the TransferTransaction() and by specifying the amount.
// Transfer token from Alice to the contract using SDK
const transferToken = new TransferTransaction()
.addTokenTransfer(tokenId, aliceId, -1000) // -10 USDB
.addTokenTransfer(tokenId, contractId, 1000) // +10 USDB
.freezeWith(client);
const transferTokenTx = await transferToken.sign(aliceKey);
const transferTokenSubmit = await transferTokenTx.execute(client);
const transferTokenRx = await transferTokenSubmit.getReceipt(client);
const transferTokenStatus = transferTokenRx.status;
console.log("The transfer transaction status: " + transferTokenStatus.toString());
Console Output:
Awesome! Now your contract has received the fungible token.
To get more details about your token transfer, you can search for your transaction on HashScan or get the contract's balance. Let's try to execute a ContractInfoQuery()!
//Check contract's balance
const query = new ContractInfoQuery()
.setContractId(contractId);
const info = await query.execute(client);
const balance = info.tokenRelationships.get(tokenId).balance / 100;
console.log("The contract balance for token " + tokenId + " is: " + balance);
Console Output:
Mission accomplished! As you can see, your contract now owns a token amount of 10.
Check out Part 2 to learn more about transferring a token.
Feel free to contact us on our Hedera Discord Server if you have questions about this article.