ABIs (Application Binary Interface) can be thought of as a restaurant menu , they describe the possible functions that can be called to interact with a smart contract.
By knowing the functions available to a contract, we can programmatically use them — in situations where the project websites are down or when you need to automate certain transactions.
For this example, we'll be using a contract specifically deployed for this tutorial on the Gnosis mainnet.
constethers=require("ethers");asyncfunctionmain() {// make an API call to the ABIs endpoint const response = await fetch('https://api.gnosisscan.io/api?module=contract&action=getabi&address=0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155&apikey=YourApiKeyToken');
constdata=awaitresponse.json();// print the JSON response let abi =data.result;console.log(abi);}main();
3. Connecting to a node
To interact with smart contracts, we will need a connection to a Gnosis node.
In this case we're using a public RPC endpoint hosted by Gnosis. You can find other public RPC endpoints onChainList.
4. Integrating Ethers.js
We'll need to integrate a JavaScript library, known as Ethers.js that will be used to interact with the Gnosis blockchain.
To do so, run the command npm i ethers from a terminal within this project directory to install it.
Ethers.js provides several classes such as a Provider, which represents the state of the Gnosis blockchain. We can create a new Provider using the syntax below, and pass in our node URL to initiate a connection to the Ethereum network.
constethers=require("ethers");asyncfunctionmain() {// make an API call to the ABIs endpoint const response = await fetch('https://api.gnosisscan.io/api?module=contract&action=getabi&address=0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155&apikey=YourApiKeyToken');
constdata=awaitresponse.json();// print the JSON response let abi =data.result;console.log(abi);// creating a new Provider, and passing in our node URL constnode="https://rpc.gnosischain.com";constprovider=newethers.providers.JsonRpcProvider(node);}main();
5. Creating a wallet
Another class that Ethers.js allows us to create is a Wallet, which will allow us to specify a private key and use a Gnosis address.
Performing write operations will incur gas costs, as such you may get some xDAI from a faucet to pay for transaction fees.
constethers=require("ethers");asyncfunctionmain() {// make an API call to the ABIs endpoint const response = await fetch('https://api.gnosisscan.io/api?module=contract&action=getabi&address=0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155&apikey=YourApiKeyToken');
constdata=awaitresponse.json();// print the JSON response let abi =data.result;console.log(abi);// creating a new Provider, and passing in our node URL constnode="https://rpc.gnosischain.com";constprovider=newethers.providers.JsonRpcProvider(node);// initiating a new Wallet, passing in our private key to sign transactionslet privatekey ="<Insert_Private_key>";let wallet =newethers.Wallet(privatekey, provider);// print the wallet addressconsole.log("Using wallet address "+wallet.address);}main();
6. Reading a contract
Finally, to interact with a smart contract we'll need to create a new Contract class.
The Contract class accepts an input of a contract address, an ABI (which we retrieved from the API earlier), and a wallet address to pay gas for any contract interactions.
constethers=require("ethers");asyncfunctionmain() {// make an API call to the ABIs endpoint const response = await fetch('https://api.gnosisscan.io/api?module=contract&action=getabi&address=0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155&apikey=YourApiKeyToken');
constdata=awaitresponse.json();// print the JSON response let abi =data.result;console.log(abi);// creating a new Provider, and passing in our node URL constnode="https://rpc.gnosischain.com";constprovider=newethers.providers.JsonRpcProvider(node);// initiating a new Wallet, passing in our private key to sign transactionslet privatekey ="<Insert_Private_key>";let wallet =newethers.Wallet(privatekey, provider);// print the wallet addressconsole.log("Using wallet address "+wallet.address);// specifying the deployed contract address let contractaddress ="0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155";// initiating a new Contractlet contract =newethers.Contract(contractaddress, abi, wallet);}main();
Having a closer look at the ABI we retrieved in Step 2, we can see that the contract has a function named read, that doesn't accept an input however does return a uint256 number as an output.
[ {"inputs": [],"name":"read"// function named read"outputs": [ { "internalType":"uint256","name":"","type":"uint256"// returns a uint256 as output } ],"stateMutability":"view","type":"function"}, {"inputs": [ {"internalType":"uint256","name":"newScore","type":"uint256" } ],"name":"write","outputs": [],"stateMutability":"nonpayable","type":"function" }]
We can therefore call that function of the contract, read the value stored and print it out.
You may run this code from your console using the command node script.js.
Reading data stored in a contract incurs no gas cost, as it does not change the state of the Gnosis blockchain.
constethers=require("ethers");asyncfunctionmain() {// make an API call to the ABIs endpoint const response = await fetch('https://api.gnosisscan.io/api?module=contract&action=getabi&address=0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155&apikey=YourApiKeyToken');
constdata=awaitresponse.json();// print the JSON response let abi =data.result;console.log(abi);// creating a new Provider, and passing in our node URL constnode="https://rpc.gnosischain.com";constprovider=newethers.providers.JsonRpcProvider(node);// initiating a new Wallet, passing in our private key to sign transactionslet privatekey ="<Insert_Private_key>";let wallet =newethers.Wallet(privatekey, provider);// print the wallet addressconsole.log("Using wallet address "+wallet.address);// specifying the deployed contract address let contractaddress ="0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155";// initiating a new Contractlet contract =newethers.Contract(contractaddress, abi, wallet);// calling the "retrieve" function to read the stored valuelet read =awaitcontract.read();console.log("Value stored in contract is "+read.toString());}main();
7. Writing a contract
Referring to the ABI once again, we can see that the contract has another method write, which accepts a uint256 number as an input and does not return any output.
[ {"inputs": [],"name":"read""outputs": [ { "internalType":"uint256","name":"","type":"uint256" } ],"stateMutability":"view","type":"function"}, {"inputs": [ {"internalType":"uint256",// requires a uint256 input"name":"newScore","type":"uint256" } ],"name":"write",// function named as write"outputs": [],// does not return an output"stateMutability":"nonpayable","type":"function" }]
We can call that function and pass in any number as a parameter. To check that its updated, we'll wait for a 2 block confirmation, and read the contract again to confirm that the number has been updated.
You may run this code from your console using the command node script.js.
constethers=require("ethers");asyncfunctionmain() {// make an API call to the ABIs endpoint const response = await fetch('https://api.gnosisscan.io/api?module=contract&action=getabi&address=0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155&apikey=YourApiKeyToken');
constdata=awaitresponse.json();// print the JSON response let abi =data.result;console.log(abi);// creating a new Provider, and passing in our node URL constnode="https://rpc.gnosischain.com";constprovider=newethers.providers.JsonRpcProvider(node);// initiating a new Wallet, passing in our private key to sign transactionslet privatekey ="<Insert_Private_key>";let wallet =newethers.Wallet(privatekey, provider);// print the wallet addressconsole.log("Using wallet address "+wallet.address);// specifying the deployed contract address let contractaddress ="0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155";// initiating a new Contractlet contract =newethers.Contract(contractaddress, abi, wallet);// calling the "retrieve" function to read the stored valuelet read =awaitcontract.read();console.log("Value stored in contract is "+read.toString());// call the "store" function to update the value to 420let write =awaitcontract.write(420);// wait for 2 blocks of confirmation write.wait(2).then(async () => { // read the contract again, similar to abovelet read =awaitcontract.read();console.log("Updated value stored in contract is "+read.toString()); });}main();
Beyond the testing grounds
The full sample code is on Github, feel free to experiment and use it ( with caution ) on real world contracts out there.
Writing new data to a contract will incur gas costs, as it requires fees to be paid to miners to process your transaction. Make sure your wallet has been funded with some xDAI
You've now mastered how to programmatically interact with smart contracts , using ABIs retrieved from the Gnosisscan APIs.
Possible use cases from this include minting NFTs right on the dot , performing trades on Decentralised Exchanges (DEXs) and automating token transfers at certain time intervals .