Web3.js
Web3.js is a Javascript library for building on EVM-compatible networks.
It allows developers to interact with smart contracts, send transactions, and retrieve data from the network.
Prerequisites
This guide assumes you have the latest version of Node.js installed.
To install web3
in your project, run the following command:
_10npm install web3
Initializing Web3 with Flow
To use web3
in your project, start by importing the module and initializing your Web3
instance with a Flow RPC endpoint.
_10const { Web3 } = require('web3');_10const web3 = new Web3('https://testnet.evm.nodes.onflow.org');
Note: If you want to connect to the Flow testnet, replace the above URL with https://mainnet.evm.nodes.onflow.org
.
Querying The Blockchain
web3
provides a number of methods for querying the blockchain, such as getting the latest block number, querying account balances, and more.
You can try using some of these methods to verify that your web3
instance is working correctly.
_15// Get the latest block number_15const blockNumber = await web3.eth.getBlockNumber();_15console.log(blockNumber); // Latest block number_15_15// Get the balance of an account_15const balance = await web3.eth.getBalance('0x1234'); // Replace with any address_15console.log(balance); // Balance in attoFlow_15_15// Get the chain ID_15const chainId = await web3.eth.getChainId();_15console.log(chainId);_15_15// Get the gas price_15const gasPrice = await web3.eth.getGasPrice();_15console.log(gasPrice); // Gas price in attoFlow
For more information about other queries you can make web3
, please see the official documentation.
Interacting with Smart Contracts
The web3
library allows developers to interact with smart contracts via the web3.eth.Contract
API.
For this example we will use the following Storage
contract.
We recommend deploying your own contract, which can be done using Hardhat or Remix.
_14// SPDX-License-Identifier: MIT_14pragma solidity ^0.8.0;_14_14contract Storage {_14 uint256 public storedData;_14_14 function store(uint256 x) public {_14 storedData = x;_14 }_14_14 function retrieve() public view returns (uint256) {_14 return storedData;_14 }_14}
The ABI for this contract can be generated using the solc
compiler, or another tool such as Hardhat or Remix.
Now that we have both the ABI and address of the contract, we can create a new Contract
object for use in our application.
_40// Replace with the ABI of the deployed contract_40const abi = [_40 {_40 inputs: [],_40 stateMutability: 'nonpayable',_40 type: 'constructor',_40 },_40 {_40 inputs: [_40 {_40 internalType: 'uint256',_40 name: 'x',_40 type: 'uint256',_40 },_40 ],_40 name: 'store',_40 outputs: [],_40 stateMutability: 'nonpayable',_40 type: 'function',_40 },_40 {_40 inputs: [],_40 name: 'retrieve',_40 outputs: [_40 {_40 internalType: 'uint256',_40 name: '',_40 type: 'uint256',_40 },_40 ],_40 stateMutability: 'view',_40 type: 'function',_40 },_40];_40_40// Replace with the address of the deployed contract_40const contractAddress = '0x4c7784ae96e7cfcf0224a95059573e96f03a4e70';_40_40// Create a new contract object with the ABI and address_40const contract = new web3.eth.Contract(abi, contractAddress);
We can now interact with the contract on the network by using the contract
object.
Reading State
State can be read from the contract by using the call
function with one of the contract's methods. This will not change the state and will not send a transaction.
_10// Retrieve the current value stored in the contract_10// (this is using the `retrieve` method from the contract with no arguments)_10const result = await contract.methods.retrieve().call();_10_10console.log(result); // Current value stored in the contract
Changing State
We can mutate the state of the contract by sending a transaction to the network.
In order to send a transaction to the network, you will need an account with sufficient funds to pay for the transaction.
If you do not have an account yet, you can create one using the following command from your project's root directory:
_10node -e "console.log(require('web3').eth.accounts.create())"
Note that this is not a secure way to generate an account, and you should use a more secure method in a production environment.
You can fund your account using the Flow Faucet.
We can use the privateKeyToAccount
function to create an Web3Account
object from our account's private key.
_10// You must replace this with the private key of the account you wish to use_10const account = web3.eth.accounts.privateKeyToAccount('0x1234');
Then, we can sign a transaction using the user's account and send it to the network.
_18const newValue = 1337; // Replace with any value you want to store_18_18// Sign a transaction that stores a new value in the contract_18// (this is using the `store` method from the contract with the new value as an argument)_18let signed = await account.signTransaction({_18 from: account.address,_18 to: contractAddress,_18 data: contract.methods.store(newValue).encodeABI(),_18 gas: 10000000n, // Replace with the gas limit you want to use_18 gasPrice: await web3.eth.getGasPrice(), // Replace with the gas price you want to use_18});_18_18// Send signed transaction to the network_18const result = await web3.eth.sendSignedTransaction(signed.rawTransaction);_18_18// { status: 1, transactionHash: '0x1234', ... }_18// status=1 means the transaction was successful_18console.log(result);
Now that the transaction has been sent, the contract's state should have been updated. We can verify this by querying the contract's state again:
_10const result = await contract.methods.retrieve().call();_10console.log(result); // New value stored in the contract
For more information about using smart contracts in web3.js, see the official documentation.