Intermediate25 min
Interacting with Smart Contracts
Read and write data to deployed smart contracts
Interacting with Smart Contracts
In this tutorial, you'll learn how to read from and write to smart contracts deployed on Beatoz.
Prerequisites
Complete Deploying Smart Contracts first.
Setting Up
const { Web3 } = require('@beatoz/web3');
require('dotenv').config();
const web3 = new Web3('https://rpc-testnet0.beatoz.io');
// Contract ABI (from compilation)
const abi = [
{
"inputs": [{"type": "uint256", "name": "initialValue"}],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "get",
"outputs": [{"type": "uint256", "name": ""}],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{"type": "uint256", "name": "newValue"}],
"name": "set",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{"indexed": false, "type": "uint256", "name": "oldValue"},
{"indexed": false, "type": "uint256", "name": "newValue"}
],
"name": "ValueChanged",
"type": "event"
}
];
const contractAddress = '0x...your_contract_address';
Creating a Contract Instance
const contract = new web3.beatoz.Contract(abi, contractAddress);
Testnet
Reading Contract Data (Call)
Reading data doesn't require gas or signing:
async function readValue() {
// Call the get() function
const value = await contract.methods.get().call();
console.log('Current value:', value);
return value;
}
Multiple Read Operations
async function readMultipleValues() {
// Execute multiple calls in parallel
const [value1, value2, value3] = await Promise.all([
contract.methods.get().call(),
contract.methods.someOtherView().call(),
contract.methods.anotherView().call(),
]);
return { value1, value2, value3 };
}
Writing Contract Data (Send)
Writing data requires gas and transaction signing:
async function setValue(newValue) {
const account = web3.beatoz.accounts.privateKeyToAccount(
process.env.PRIVATE_KEY
);
// Encode the function call
const data = contract.methods.set(newValue).encodeABI();
// Get account info for nonce
const accountInfo = await web3.beatoz.getAccount(account.address);
const status = await web3.beatoz.status();
// Build transaction
const tx = TrxProtoBuilder.buildContractTrxProto({
from: account.address,
to: contractAddress,
nonce: accountInfo.nonce + 1,
gas: 100000,
gasPrice: web3.utils.toFons('0.000001'),
data: data,
chainId: status.node_info.network,
});
// Sign and broadcast
const signedTx = account.signTransaction(tx);
const result = await web3.beatoz.broadcastTxCommit(signedTx);
if (result.deliver_tx.code !== 0) {
throw new Error(`Transaction failed: ${result.deliver_tx.log}`);
}
console.log('Value updated! TX:', result.hash);
return result;
}
Gas Estimation
Always estimate gas before sending transactions:
async function estimateSetGas(newValue) {
const account = web3.beatoz.accounts.privateKeyToAccount(
process.env.PRIVATE_KEY
);
const gasEstimate = await contract.methods.set(newValue).estimateGas({
from: account.address,
});
console.log('Estimated gas:', gasEstimate);
// Add 20% buffer for safety
return Math.ceil(gasEstimate * 1.2);
}
Using Contract.send()
The SDK provides a convenient send() method:
async function setValueEasy(newValue) {
const account = web3.beatoz.accounts.privateKeyToAccount(
process.env.PRIVATE_KEY
);
const receipt = await contract.methods.set(newValue).send({
from: account.address,
gas: 100000,
gasPrice: web3.utils.toFons('0.000001'),
});
console.log('Transaction hash:', receipt.hash);
return receipt;
}
Working with Complex Types
Arrays
// Solidity: function setNumbers(uint256[] memory nums)
await contract.methods.setNumbers([1, 2, 3, 4, 5]).send({
from: account.address,
gas: 200000,
});
// Reading arrays
const numbers = await contract.methods.getNumbers().call();
console.log('Numbers:', numbers); // [1, 2, 3, 4, 5]
Structs
// Solidity struct: struct Person { string name; uint256 age; }
// Solidity: function setPerson(Person memory p)
await contract.methods.setPerson({
name: "Alice",
age: 25,
}).send({ from: account.address, gas: 200000 });
Addresses
await contract.methods.setOwner('0x...address').send({
from: account.address,
gas: 100000,
});
Encoding and Decoding
Encode Function Call
const data = contract.methods.set(100).encodeABI();
console.log('Encoded data:', data);
// 0x60fe47b10000000000000000000000000000000000000000000000000000000000000064
Decode Return Value
const encoded = '0x0000000000000000000000000000000000000000000000000000000000000064';
const decoded = web3.beatoz.abi.decodeParameter('uint256', encoded);
console.log('Decoded value:', decoded); // 100
Complete Example
const { Web3, TrxProtoBuilder } = require('@beatoz/web3');
require('dotenv').config();
async function main() {
const web3 = new Web3('https://rpc-testnet0.beatoz.io');
const account = web3.beatoz.accounts.privateKeyToAccount(
process.env.PRIVATE_KEY
);
const abi = [/* ... your ABI ... */];
const contractAddress = '0x...';
const contract = new web3.beatoz.Contract(abi, contractAddress);
// 1. Read current value
const currentValue = await contract.methods.get().call();
console.log('Current value:', currentValue);
// 2. Estimate gas for new value
const newValue = parseInt(currentValue) + 1;
const gas = await contract.methods.set(newValue).estimateGas({
from: account.address,
});
console.log('Estimated gas:', gas);
// 3. Update value
const receipt = await contract.methods.set(newValue).send({
from: account.address,
gas: Math.ceil(gas * 1.2),
});
console.log('Updated! TX:', receipt.hash);
// 4. Verify update
const updatedValue = await contract.methods.get().call();
console.log('New value:', updatedValue);
}
main().catch(console.error);
You've learned how to interact with smart contracts on Beatoz!
Common Errors
| Error | Cause | Solution |
|---|---|---|
| "execution reverted" | Contract logic failed | Check your input values |
| "out of gas" | Gas limit too low | Increase gas limit |
| "invalid nonce" | Nonce mismatch | Fetch fresh nonce |
| "insufficient balance" | Not enough BTZ | Get tokens from Faucet |
