Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit 8c3a17b

Browse files
Add a tutorial for smart contract basic interaction (#6089)
* a tutorial for smart contract basic interaction * add tutorials section to the docs sidebar * tiny text update in docs * update yarn.lock for docusaurus * update docusaurus to version 2.4.1
1 parent edc7a84 commit 8c3a17b

File tree

5 files changed

+403
-12
lines changed

5 files changed

+403
-12
lines changed
Lines changed: 384 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,384 @@
1+
---
2+
sidebar_position: 1
3+
sidebar_label: 'Deploying and Interacting with Smart Contracts'
4+
---
5+
6+
# Deploying and Interacting with Smart Contracts
7+
8+
## Introduction
9+
10+
In this tutorial, we will walk through the process of deploying a smart contract to the Ethereum network, generating the ABI, and interacting with the smart contract using web3.js version 4.x. We will cover the basic concepts of Ethereum, Solidity, and web3.js and provide step-by-step instructions for deploying a simple smart contract to a test network using Ganache.
11+
12+
## Overview
13+
14+
Here is a high-level overview of the steps we will be taking in this tutorial:
15+
16+
1. Setting up the Environment
17+
2. Create a new project directory and initialize a new Node.js project.
18+
3. Write the Solidity code for the smart contract and save it to a file.
19+
4. Compile the Solidity code using the Solidity Compiler and get its ABI and Bytecode.
20+
5. Set up the web3.js library and connect to the Ganache network.
21+
6. Deploy the smart contract to the Ganache network using web3.js.
22+
7. Interact with the smart contract using web3.js.
23+
24+
## Step 1: Setting up the Environment
25+
26+
Before we start writing and deploying our contract, we need to set up our environment. For that, we need to install the following:
27+
28+
1. Ganache - Ganache is a personal blockchain for Ethereum development that allows you to see how your smart contracts function in real-world scenarios. You can download it from http://truffleframework.com/ganache
29+
2. Node.js - Node.js is a JavaScript runtime environment that allows you to run JavaScript on the server-side. You can download it from https://nodejs.org/en/download/
30+
3. npm - Node Package Manager is used to publish and install packages to and from the public npm registry or a private npm registry. Here is how to install it https://docs.npmjs.com/downloading-and-installing-node-js-and-npm. (Alternatively, you can use yarn instead of npm https://classic.yarnpkg.com/lang/en/docs/getting-started/)
31+
32+
## Step 2: Create a new project directory and initialize a new Node.js project
33+
34+
First, create a new project directory for your project and navigate into it:
35+
36+
```
37+
mkdir smart-contract-tutorial
38+
cd smart-contract-tutorial
39+
```
40+
41+
Next, initialize a new Node.js project using npm:
42+
43+
```
44+
npm init -y
45+
```
46+
47+
This will create a new `package.json` file in your project directory.
48+
49+
## Step 3: Write the Solidity code for the smart contract and save it to a file
50+
51+
In this step, we will write the Solidity code for the smart contract and save it as a file in our project directory.
52+
53+
Create a new file called `MyContract.sol` in your project directory and add the following Solidity code to it:
54+
55+
```solidity
56+
// SPDX-License-Identifier: MIT
57+
pragma solidity ^0.8.0;
58+
59+
contract MyContract {
60+
uint256 public myNumber;
61+
62+
constructor(uint256 _myNumber) {
63+
myNumber = _myNumber;
64+
}
65+
66+
function setMyNumber(uint256 _myNumber) public {
67+
myNumber = _myNumber;
68+
}
69+
}
70+
71+
```
72+
73+
This simple smart contract defines a `myNumber` variable that can be set by calling the `setMyNumber` function.
74+
75+
## Step 4: Compile the Solidity code using the Solidity Compiler and get its ABI and Bytecode.
76+
77+
:::tip
78+
📝 Alternatively, you can use something like `npm i solc && npx solcjs MyContract.sol --bin --abi`. And then rename the files to `MyContractBytecode.bin` and `MyContractAbi.json`, in order to keep them the same as they will be used later in this tutorial.
79+
More on solc-js is at https://github.com/ethereum/solc-js
80+
:::
81+
82+
In this step, we will use the Solidity Compiler (solc) to compile the Solidity code and generate the compiled code.
83+
84+
First, install the `solc` package using npm.
85+
86+
:::note
87+
📝 Specify a version for the compiler that is compatible with the version you specified in the .sol file above (with `pragma solidity ^0.8.0;`):
88+
:::
89+
90+
```
91+
npm install solc@0.8.0
92+
```
93+
94+
Next, create a new file called `compile.js` in your project directory and add the following code to it:
95+
96+
```javascript
97+
// This code will compile smart contract and generate its ABI and bytecode
98+
// Alternatively, you can use something like `npm i solc && npx solcjs MyContract.sol --bin --abi`
99+
100+
import solc from 'solc';
101+
import path from 'path';
102+
import fs from 'fs';
103+
104+
const fileName = 'MyContract.sol';
105+
const contractName = 'MyContract';
106+
107+
// Read the Solidity source code from the file system
108+
const contractPath = path.join(__dirname, fileName);
109+
const sourceCode = fs.readFileSync(contractPath, 'utf8');
110+
111+
// solc compiler config
112+
const input = {
113+
language: 'Solidity',
114+
sources: {
115+
[fileName]: {
116+
content: sourceCode,
117+
},
118+
},
119+
settings: {
120+
outputSelection: {
121+
'*': {
122+
'*': ['*'],
123+
},
124+
},
125+
},
126+
};
127+
128+
// Compile the Solidity code using solc
129+
const compiledCode = JSON.parse(solc.compile(JSON.stringify(input)));
130+
131+
// Get the bytecode from the compiled contract
132+
const bytecode = compiledCode.contracts[fileName][contractName].evm.bytecode.object;
133+
134+
// Write the bytecode to a new file
135+
const bytecodePath = path.join(__dirname, 'MyContractBytecode.bin');
136+
fs.writeFileSync(bytecodePath, bytecode);
137+
138+
// Log the compiled contract code to the console
139+
console.log('Contract Bytecode:\n', bytecode);
140+
141+
// Get the ABI from the compiled contract
142+
const abi = compiledCode.contracts[fileName][contractName].abi;
143+
144+
// Write the Contract ABI to a new file
145+
const abiPath = path.join(__dirname, 'MyContractAbi.json');
146+
fs.writeFileSync(abiPath, JSON.stringify(abi, null, '\t'));
147+
148+
// Log the Contract ABI to the console
149+
console.log('Contract ABI:\n', abi);
150+
```
151+
152+
This code reads the Solidity code from the `MyContract.sol` file, compiles it using `solc`, and generates the ABI and bytecode for the smart contract. It then writes the bytecode to a new file called `MyContractBytecode.bin` and the contract ABI to `MyContractAbi.json`. And it logs them to the console.
153+
154+
Run the following command to compile the Solidity code:
155+
156+
```
157+
node compile.js
158+
```
159+
160+
If everything is working correctly, you should see both the Contract Bytecode and the Contract ABI logged to the console.
161+
162+
:::tip
163+
📝 There are couple of other ways to get the bytecode and the ABI like using Truffle development framework and running `truffle compile` (https://trufflesuite.com/docs/truffle/quickstart/#compile).
164+
Another way is to use Remix and check the _Compilation Details_ after compiling the smart contract (https://remix-ide.readthedocs.io/en/latest/run.html#using-the-abi-with-ataddress)
165+
:::
166+
167+
## Step 5: Set up web3.js and connect to the Ganache network
168+
169+
In this step, we will set up the web3.js library and connect to the Ganache network. So, be sure to run Ganache if you did not already did.
170+
171+
First, install the `web3` package using npm:
172+
173+
```
174+
npm install web3@4.0.1-rc.1
175+
```
176+
177+
Note that we are installing the latest version of 4.x, at the time of this tutorial writing. You can check the latest version at https://www.npmjs.com/package/web3?activeTab=versions
178+
179+
Next, create a new file called `index.js` in your project directory and add the following code to it:
180+
181+
```javascript
182+
const { Web3 } = require('web3'); // web3.js has native ESM builds and (`import { Web3 } from 'web3'`)
183+
184+
// Set up a connection to the Ganache network
185+
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:7545'));
186+
187+
// Log the current block number to the console
188+
web3.eth
189+
.getBlockNumber()
190+
.then(result => {
191+
console.log('Current block number: ' + result);
192+
})
193+
.catch(error => {
194+
console.error(error);
195+
});
196+
```
197+
198+
This code sets up a connection to the Ganache network and logs the current block number to the console.
199+
200+
Run the following command to test the connection:
201+
202+
```
203+
node index.js
204+
```
205+
206+
If everything is working correctly, you should see the current block number logged to the console. However, if you got an error with the reason `connect ECONNREFUSED 127.0.0.1:7545` then double check that you are running Ganache locally on port `7545`.
207+
208+
## Step 5: Deploy the smart contract to the Ganache network using web3.js
209+
210+
In this step, we will use web3.js to deploy the smart contract to the Ganache network.
211+
212+
Create a file named `deploy.js` and fill it with the following code:
213+
214+
```javascript
215+
// For simplicity we use `web3` package here. However, if you are concerned with the size,
216+
// you may import individual packages like 'web3-eth', 'web3-eth-contract' and 'web3-providers-http'.
217+
const { Web3 } = require('web3'); // web3.js has native ESM builds and (`import { Web3 } from 'web3'`)
218+
const fs = require('fs');
219+
const path = require('path');
220+
221+
// Set up a connection to the Ethereum network
222+
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:7545'));
223+
web3.eth.Contract.handleRevert = true;
224+
225+
// Read the bytecode from the file system
226+
const bytecodePath = path.join(__dirname, 'MyContractBytecode.bin');
227+
const bytecode = fs.readFileSync(bytecodePath, 'utf8');
228+
229+
// Create a new contract object using the ABI and bytecode
230+
const abi = require('./MyContractAbi.json');
231+
const MyContract = new web3.eth.Contract(abi);
232+
233+
async function deploy() {
234+
const providersAccounts = await web3.eth.getAccounts();
235+
const defaultAccount = providersAccounts[0];
236+
console.log('deployer account:', defaultAccount);
237+
238+
const myContract = MyContract.deploy({
239+
data: '0x' + bytecode,
240+
arguments: [1],
241+
});
242+
243+
// optionally, estimate the gas that will be used for development and log it
244+
const gas = await myContract.estimateGas({
245+
from: defaultAccount,
246+
});
247+
console.log('estimated gas:', gas);
248+
249+
try {
250+
// Deploy the contract to the Ganache network
251+
const tx = await myContract.send({
252+
from: defaultAccount,
253+
gas,
254+
gasPrice: 10000000000,
255+
});
256+
console.log('Contract deployed at address: ' + tx.options.address);
257+
258+
// Write the Contract address to a new file
259+
const deployedAddressPath = path.join(__dirname, 'MyContractAddress.bin');
260+
fs.writeFileSync(deployedAddressPath, tx.options.address);
261+
} catch (error) {
262+
console.error(error);
263+
}
264+
}
265+
266+
deploy();
267+
```
268+
269+
This code reads the bytecode from the `MyContractBytecode.bin` file and creates a new contract object using the ABI and bytecode. And, as an optional step, it estimates the gas that will be used to deploy the smart contract. It then deploys the contract to the Ganache network. It also saves the address inside the file `MyContractAddress.bin` which we be used when interacting with the contract.
270+
271+
Run the following command to deploy the smart contract:
272+
273+
```sh
274+
node deploy.js
275+
```
276+
277+
If everything is working correctly, you should see something like the following:
278+
279+
```
280+
Deployer account: 0xdd5F9948B88608a1458e3a6703b0B2055AC3fF1b
281+
Estimated gas: 142748n
282+
Contract deployed at address: 0x16447837D4A572d0a8b419201bdcD91E6e428Df1
283+
```
284+
285+
## Step 6: Interact with the smart contract using web3.js
286+
287+
In this step, we will use web3.js to interact with the smart contract on the Ganache network.
288+
289+
Create a file named `interact.js` and fill it with the following code:
290+
291+
```javascript
292+
const { Web3 } = require('web3'); // web3.js has native ESM builds and (`import { Web3 } from 'web3'`)
293+
const fs = require('fs');
294+
const path = require('path');
295+
296+
// Set up a connection to the Ethereum network
297+
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:7545'));
298+
web3.eth.Contract.handleRevert = true;
299+
300+
// Read the contract address from the file system
301+
const deployedAddressPath = path.join(__dirname, 'MyContractAddress.bin');
302+
const deployedAddress = fs.readFileSync(deployedAddressPath, 'utf8');
303+
304+
// Read the bytecode from the file system
305+
const bytecodePath = path.join(__dirname, 'MyContractBytecode.bin');
306+
const bytecode = fs.readFileSync(bytecodePath, 'utf8');
307+
308+
// Create a new contract object using the ABI and bytecode
309+
const abi = require('./MyContractAbi.json');
310+
const MyContract = new web3.eth.Contract(abi, deployedAddress);
311+
312+
async function interact() {
313+
const providersAccounts = await web3.eth.getAccounts();
314+
const defaultAccount = providersAccounts[0];
315+
316+
try {
317+
// Get the current value of my number
318+
const myNumber = await MyContract.methods.myNumber().call();
319+
console.log('my number value: ' + myNumber);
320+
321+
// Increment my number
322+
const receipt = await MyContract.methods.setMyNumber(myNumber + 1n).send({
323+
from: defaultAccount,
324+
gas: 1000000,
325+
gasPrice: 10000000000,
326+
});
327+
console.log('Transaction Hash: ' + receipt.transactionHash);
328+
329+
// Get the updated value of my number
330+
const myNumberUpdated = await MyContract.methods.myNumber().call();
331+
console.log('my number updated value: ' + myNumberUpdated);
332+
} catch (error) {
333+
console.error(error);
334+
}
335+
}
336+
337+
interact();
338+
```
339+
340+
This code uses the `MyContract` object to interact with the smart contract. It gets the current value of myNumber, increments it and update it, and gets its updated value. It logs myNumber values and transaction receipts to the console.
341+
342+
Run the following command to interact with the smart contract:
343+
344+
```
345+
node interact.js
346+
```
347+
348+
If everything is working correctly, you should see the current counter value logged to the console, followed by the transaction receipt, and then the updated counter value. The output would like:
349+
350+
```sh
351+
my number value: 1
352+
Transaction Hash: 0x9825e2a2115896728d0c9c04c2deaf08dfe1f1ff634c4b0e6eeb2f504372f927
353+
my number updated value: 2
354+
```
355+
356+
## Conclusion
357+
358+
In this tutorial, we learned how to generate the ABI and the Bytecode of a smart contract, deploy it to the Ethereum network, and interact with it using web3.js version 4.x.
359+
360+
With this knowledge, you can start experimenting with writing smart contract in order for building your decentralized applications (dApps) on the Ethereum network using web3.js. Keep in mind that this is just the beginning, and there is a lot more to learn about Ethereum and web3.js. So keep exploring and building, and have fun!
361+
362+
## Additional Resources
363+
364+
- [Official web3.js Documentation](https://docs.web3js.org/)
365+
- [Solidity Documentation](https://solidity.readthedocs.io/)
366+
- [Ganache](https://www.trufflesuite.com/ganache)
367+
- [Truffle](https://trufflesuite.com/)
368+
- [Remix IDE](https://remix.ethereum.org/)
369+
370+
## Tips and Best Practices
371+
372+
- Always test your smart contracts on a local network like Ganache before deploying them to the mainnet.
373+
- Use the latest version of web3.js and Solidity to take advantage of the latest features and security patches.
374+
- Keep your private keys secure and never share them with anyone.
375+
- Use the gas limit and gas price parameters carefully to avoid spending too much on transaction fees.
376+
- Use the `estimateGas` function in web3.js to estimate the gas required for a transaction before sending it to the network.
377+
- Use events to notify the client application about state changes in the smart contract.
378+
- Use a linter like Solhint to check for common Solidity coding errors.
379+
380+
## Final Thoughts
381+
382+
Web3.js version 4.x provides a powerful and easy-to-use interface for interacting with the Ethereum network and building decentralized applications. And it has been rewritten in TypeScript but for simplicity of this tutorial we interacted with it in JavaScript.
383+
384+
The Ethereum ecosystem is constantly evolving, and there is always more to learn and discover. As you continue to develop your skills and knowledge, keep exploring and experimenting with new technologies and tools to build innovative and decentralized solutions.

docs/docusaurus.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ const config = {
127127
label: 'Guides',
128128
position: 'left',
129129
},
130+
{
131+
to: 'docs/tutorials/deploying_and_interacting_with_smart_contracts',
132+
activeBasePath: 'docs/tutorials',
133+
label: 'Tutorials',
134+
position: 'left',
135+
},
130136
{
131137
to: 'api', // 'api' is the 'out' directory
132138
label: 'API',

0 commit comments

Comments
 (0)