Smart contracts on Hedera can hold and exchange value in the form of HBAR, Hedera Token Service (HTS) tokens, and even ERC tokens. This is fundamental for building decentralized applications that rely on contracts in areas like DeFi, ESG, NFT marketplaces, DAOs, and more.
In this tutorial, you will learn how to send and receive HBAR to and from Hedera contracts. At a high level, there are two ways to transfer HBAR to and from a contract on Hedera: the SDKs and Solidity.
Part 1 focuses on using the Hedera SDKs. Read Part 2 for transferring HBAR to and from contracts using Solidity.
Here are a few key points about transferring HBAR to and from contracts using the SDKs:
This example has three entities: the operator, Alice, and the contract. Your testnet credentials should be used for the operator variables, which are used to initialize the Hedera client that submits transactions to the network and gets confirmations. Create Alice’s account with an initial balance of 100 HBAR, and then Alice will transfer 10 HBAR to the smart contract using the TransferTransaction() module in the SDK.
Below is the Solidity code for the contract. You can get the bytecode from Codesandbox, the GitHub repository, or by compiling the code.
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; contract hbar2Contract{ function getBalance() public view returns (uint) { return address(this).balance; } }
Generate a private key for Alice. Hedera supports ED25519 and ECDSA keys.
const aliceKey = PrivateKey.generateECDSA();
Create Alice’s account with a balance of 100 HBAR. The function accountCreatorFcn simplifies the account creation process and is reusable in case you need to create more accounts in the future. This function uses the AccountCreateTransaction() module. We’ll use this modular approach throughout the article.
// Create additional accounts needed const initialBalance = 100; const [accStatus, aliceId] = await accountCreatorFcn(aliceKey, initialBalance); console.log( `\n- Created Alice's account with initial balance of ${initialBalance} hbar: ${accStatus}` );
async function accountCreatorFcn(pvKey, iBal) { const response = await new AccountCreateTransaction() .setInitialBalance(new Hbar(iBal)) .setKey(pvKey.publicKey) .setAlias(pvKey.publicKey.toEvmAddress()) .execute(client); const receipt = await response.getReceipt(client); return [receipt.status, receipt.accountId]; }
Console Output:
- Created Alice's account with initial balance of 100 hbar: SUCCESS
The compiled contract bytecode is a binary contained in the variable contractBytecode. The function contractCreatorFcn uses the ContractCreateFlow() module and returns the contract ID and corresponding Solidity address for the contract.
// Import the compiled contract bytecode const contractBytecode = fs.readFileSync("transferHbar2Contract_sdk_sol_hbar2Contract.bin"); // Deploy the contract on Hedera const [contractId, contractAddress] = await contractCreatorFcn(contractBytecode); console.log(`\n- The smart contract ID is: ${contractId}`); console.log(`- The smart contract ID in Solidity format is: ${contractAddress}`);
ContractCreateFlow() stores the bytecode and deploys the contract on Hedera. This single call handles for you the operations FileCreateTransaction(), FileAppendTransaction(), and ContractCreateTransaction().
Set a gas value that is enough to execute the transaction; otherwise, you'll get the error CONTRACT_REVERT_EXECUTED.
async function contractCreatorFcn(contractBytecode) { const contractDeployTx = await new ContractCreateFlow() .setBytecode(contractBytecode) .setGas(100000) .execute(client); const contractDeployRx = await contractDeployTx.getReceipt(client); const contractId = contractDeployRx.contractId; const contractAddress = contractId.toSolidityAddress(); return [contractId, contractAddress]; }
- The smart contract ID is: 0.0.47716894
- The smart contract ID in Solidity format is: 0000000000000000000000000000000002d81a1e
Transfer 10 HBAR to the contract from Alice’s account using the function hbarTransferFcn.
// Transfer HBAR to smart contract using TransferTransaction() const hbarAmount = 10; const transferRx = await hbarTransferFcn(aliceId, contractId, hbarAmount); console.log(`\n- Transfer ${hbarAmount} HBAR from Alice to contract: ${transferRx.status}`);
Use the TransferTransaction() module to transfer the HBAR. Remember that the account for which the balance is deducted must sign the transfer transaction (Alice in this case).
async function hbarTransferFcn(sender, receiver, amount) { const transferTx = new TransferTransaction() .addHbarTransfer(sender, -amount) .addHbarTransfer(receiver, amount) .freezeWith(client); const transferSign = await transferTx.sign(aliceKey); const transferSubmit = await transferSign.execute(client); const transferRx = await transferSubmit.getReceipt(client); return transferRx; }
- Transfer 10 HBAR from Alice to contract: SUCCESS
Finally, use the function contractBalanceCheckerFcn to check the HBAR balance of the contract. This function checks the balance in two ways: 1) calling the getBalance function in the contract via a ContractCallQuery(), and 2) using the ContractInfoQuery() module of the SDK.
// Query the contract balance const [fromCallQuery, fromInfoQuery] = await contractBalanceCheckerFcn(contractId); console.log(`\n- Contract balance (from getBalance fcn): ${fromCallQuery} tinybars`); console.log(`- Contract balance (from ContractInfoQuery): ${fromInfoQuery.balance.toString()}`);
async function contractBalanceCheckerFcn(contractId) { const contractQueryTx = new ContractCallQuery() .setContractId(contractId) .setGas(100000) .setFunction("getBalance"); const contractQuerySubmit = await contractQueryTx.execute(client); const contractQueryResult = contractQuerySubmit.getUint256(0); const cCheck = await new ContractInfoQuery().setContractId(contractId).execute(client); return [contractQueryResult, cCheck]; }
- Contract balance (from getBalance fcn): 1000000000 tinybars
- Contract balance (from ContractInfoQuery): 10 ℏ
Now you know how to send HBAR to a contract on Hedera using the TransferTransaction() module of the SDK.
You can also send HBAR from a contract using the SDK. However, the contract sending the HBAR must have an admin key to sign the TransferTransaction().
For contracts without admin keys, be sure to read Part 2. There you’ll learn how to transfer HBAR to/from contracts using Solidity.