Back to Tutorials
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

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

ErrorCauseSolution
"execution reverted"Contract logic failedCheck your input values
"out of gas"Gas limit too lowIncrease gas limit
"invalid nonce"Nonce mismatchFetch fresh nonce
"insufficient balance"Not enough BTZGet tokens from Faucet

Next Steps

Beatoz Developer Portal