Skip to content

Commit dd25a72

Browse files
Merge branch 'main' into sharjeel
2 parents 4656a1e + 6b47e96 commit dd25a72

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+48969
-1
lines changed

projects.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ We want to create a Payment Splitter smart contract that will distribute the pay
7373

7474
[HOW to Use ARBITRUM?! Is it All Just Hype?!](https://www.youtube.com/watch?v=LNBSPMQ-xr4)
7575

76-
[](https://www.youtube.com/watch?v=H32dTx7NjzI)
76+
[How To Add Arbitrum Network To MetaMask & Bridge Ethereum](https://www.youtube.com/watch?v=H32dTx7NjzI)
77+
78+
[The comparison of Gas fees on Layer 2](https://l2fees.info/)
7779

7880
[Arbitrum Developer Quickstart](https://developer.offchainlabs.com/docs/developer_quickstart)
7981

Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
# Web3 with ReactJS
2+
3+
Lets create folders named *backend* and *frontend*
4+
5+
create a react typescript app inside *frontend* folder
6+
7+
> npx create-react-app my-app --template typescript -frontend
8+
9+
this will create a react project with following files and folder
10+
11+
![Frontend folder structure.](./image/frontend.png "Frontend folder structure.")
12+
13+
Before connecting frontend with web3 we need to create a smart contract
14+
15+
now lets create a truffle project inside our backend folder
16+
17+
> npm install -g truffle ( if not installed already)
18+
> truffle init
19+
20+
this will create a truffle project with following files and folder
21+
22+
![Backend folder structure.](./image/backend.png "Backend folder structure.")
23+
24+
change truffle config, uncomment the following
25+
```javascript
26+
networks: {
27+
development: {
28+
host: "127.0.0.1", // Localhost (default: none)
29+
port: 8545, // Standard Ethereum port (default: none)
30+
network_id: "*", // Any network (default: none)
31+
},
32+
```
33+
34+
update solidity compiler version
35+
```javascript
36+
compilers: {
37+
solc: {
38+
version: "0.8.6", // Fetch exact version from solc-bin (default: truffle's version)
39+
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
40+
// settings: { // See the solidity docs for advice about optimization and evmVersion
41+
optimizer: {
42+
enabled: false,
43+
runs: 200
44+
},
45+
// evmVersion: "byzantium"
46+
// }
47+
}
48+
},
49+
50+
```
51+
add the following to mention the build directory storing contracts ABI inside frontend's folder
52+
```javascript
53+
{
54+
contracts_directory: './contracts/',
55+
contracts_build_directory: '../frontend/src/abis/',
56+
}
57+
```
58+
59+
- create a file in contracts folder *Marketplace.sol* to write our smart contract
60+
- add the following code:
61+
62+
```javascript
63+
pragma solidity ^0.8.6;
64+
65+
contract Marketplace {
66+
string public name;
67+
uint public productCount = 0;
68+
69+
struct Product {
70+
uint id;
71+
string name;
72+
uint price;
73+
address payable owner;
74+
bool purchased;
75+
}
76+
mapping(uint => Product) public products;
77+
78+
constructor(){
79+
name = "Marketplace";
80+
}
81+
}
82+
```
83+
we are creating a contract with
84+
- a string variable *name*.
85+
- a uint variable *productCount*
86+
- a struct named Product
87+
- a map named products having *id* and *Product* as key value.
88+
89+
Next add tow functions to our code *createProduct*: creates a new product in blockchain and *purchaseProduct*: to make a transaction, transfering the ownership of the product to the buyer and pay the amount to the product creator.
90+
91+
```javascript
92+
function createProduct(string memory _name, uint _price) public { // Require a valid name
93+
require(bytes(_name).length > 0);
94+
// Require a valid price
95+
require(_price > 0);
96+
// Increment product count
97+
productCount ++;
98+
// Create the product
99+
// msg.sender is the address of the user creating the product.
100+
products[productCount] = Product(productCount, _name, _price,payable(msg.sender), false);
101+
}
102+
103+
function purchaseProduct(uint _id) public payable {
104+
// Fetch the product
105+
Product memory _product = products[_id];
106+
// Fetch the owner
107+
address payable _seller = _product.owner;
108+
// Make sure the product has a valid id
109+
require(_product.id > 0 && _product.id <= productCount);
110+
// Require that there is enough Ether in the transaction
111+
require(msg.value >= _product.price);
112+
// Require that the product has not been purchased already
113+
require(!_product.purchased);
114+
// Require that the buyer is not the seller
115+
require(_seller != msg.sender);
116+
// Transfer ownership to the buyer
117+
_product.owner = payable(msg.sender);
118+
// Mark as purchased
119+
_product.purchased = true;
120+
// Update the product
121+
products[_id] = _product;
122+
// Pay the seller by sending them Ether
123+
_seller.transfer(msg.value);
124+
}
125+
```
126+
Finally add the events to the code
127+
128+
```javascript
129+
event ProductCreated(
130+
uint id,
131+
string name,
132+
uint price,
133+
address payable owner,
134+
bool purchased
135+
);
136+
137+
event ProductPurchased(
138+
uint id,
139+
string name,
140+
uint price,
141+
address payable owner,
142+
bool purchased
143+
);
144+
145+
```
146+
and add the emitter to the respective functions
147+
148+
```javascript
149+
emit ProductCreated(productCount, _name, _price, payable(msg.sender), false);
150+
```
151+
in the createProduct function, after product is created successfully and
152+
```javascript
153+
emit ProductPurchased(productCount, _product.name, _product.price, payable(msg.sender), true);
154+
```
155+
in the purchaseProduct function, after transaction is successfull
156+
157+
now lets complile the code
158+
> truffle compile
159+
160+
Finally we need to deploy this smart contract to Ganache
161+
> create a file *2_deploy_contracts.js* in migrations folder
162+
163+
This file tells Truffle to to deploy our smart contract to the blockchain, numbered to order the run.
164+
165+
add the following :
166+
```javascript
167+
const Marketplace = artifacts.require("Marketplace");
168+
169+
module.exports = function(deployer) {
170+
deployer.deploy(Marketplace);
171+
};
172+
173+
```
174+
Now run
175+
> truffle migrate --reset
176+
177+
# Smart contract tests
178+
179+
Create a file * Marketplace.test.js* for the smart contract tests in the *tests* folder:
180+
181+
install the dependencies for testing.
182+
> npm i chai chai-as-promised
183+
184+
add tools to our test suite
185+
186+
```javascript
187+
require('chai')
188+
.use(require('chai-as-promised'))
189+
.should()
190+
191+
```
192+
193+
we have created tests for the following:
194+
- Contract is deployed successfully
195+
- Products are created
196+
- Products are listing as expected
197+
- Products are sold with proper transaction
198+
199+
Now test them.
200+
> truffle test
201+
202+
lets compile with
203+
204+
> truffle migrate --reset
205+
206+
![Output.](./image/bcc1.png "Deployed.")
207+
208+
209+
# Now move on to frontend
210+
211+
install metamask to your browser
212+
213+
Download Ganache from https://www.trufflesuite.com/ganache
214+
215+
Connect Metamask to our Ganache personal blockchain instance following the steps
216+
- start Ganache
217+
218+
- click on new workspace
219+
![new workspace.](./image/g1.png "new workspace.")
220+
221+
- go to server section
222+
![server section.](./image/g2.png "server section.")
223+
224+
- change port number to 8545
225+
![image of changing port.](./image/g3.png "image of changing port.")![image of changing port.](./image/g5.png "image of changing port.")
226+
227+
- now click on save workspace
228+
![save workspace.](./image/g6.png "save workspace")
229+
230+
- then open metamask in your browser
231+
- create a new account
232+
233+
- click on ethereum mainet
234+
![networks image.](./image/m2.png "networks")
235+
236+
- in the dropdown menu
237+
- select localhost:8545
238+
![image of changing network.](./image/m3.png "changing network.")
239+
240+
- now you are connected
241+
242+
- go to ganache and choose any account you want and click on key
243+
![image of key.](./image/m5.png "select key.")
244+
245+
- select the whole key and copy it
246+
![image of selected key.](./image/m6.png "select whole key")
247+
248+
- go to metamask
249+
- click on the account icon
250+
![account icon](./image/m7.png "account icon")
251+
252+
- then select import account from dropdown menu
253+
![import account image](./image/m8.png "import account")
254+
255+
- paste the copied key
256+
- and click import
257+
![image of key and click import](./image/m9.png "paste key and import")
258+
259+
- you should see the ethereum balance (note that this ethereum has no value, its just for testing)
260+
261+
## Client Side application
262+
263+
install web3.js
264+
265+
> npm i web3
266+
267+
install typechain for generating types of contract
268+
269+
> npm i typechain @typechain/web3-v1
270+
271+
- add the scripts for generating types
272+
```json
273+
"scripts": {
274+
"start": "react-scripts start",
275+
"build": "react-scripts build",
276+
"test": "react-scripts test",
277+
"eject": "react-scripts eject",
278+
"generate-types": "typechain --target=web3-v1 \"./src/abi/*.json\"" ,
279+
"postinstall": "yarn generate-types"
280+
}
281+
282+
```
283+
- generate types by running command *npm generate-types*
284+
this will create types folder inside src directory
285+
286+
Start coding, add the following code in App.js
287+
288+
```javascript
289+
import Web3 from 'web3'
290+
```
291+
292+
- instantiate web3.
293+
294+
```javascript
295+
const loadWeb3 = async () => {
296+
if (window.ethereum) {
297+
window.web3 = new Web3(window.ethereum);
298+
try{
299+
// Request account access if needed
300+
await window.ethereum.enable();
301+
}catch (error) {
302+
window.alert(
303+
"User denied account access...!"
304+
);
305+
}
306+
}else if (window.web3) {
307+
window.web3 = new Web3(window.web3.currentProvider);
308+
} else {
309+
window.alert(
310+
"Non-Ethereum browser detected. You should consider trying MetaMask!"
311+
);
312+
}
313+
};
314+
315+
```
316+
Once web3 is initialized, lets get the data from smart contract,
317+
318+
```javascript
319+
const loadBlockchainData = async () => {
320+
const web3: Web3 = window.web3;
321+
const accounts = await web3.eth.getAccounts();
322+
try {
323+
//Read the networkID to determine the network
324+
const networkId : number= await web3.eth.net.getId();
325+
const netId = networkId as unknown as keyof typeof Marketplace.networks
326+
const networkData = Marketplace.networks[netId];
327+
if (networkData) {
328+
//instantiate the smart contract
329+
const marketContract = await new web3.eth.Contract(
330+
Marketplace.abi as AbiItem[],
331+
networkData.address
332+
) as unknown as MPType
333+
334+
const productCount = await marketContract.methods.productCount().call() as unknown as number
335+
// Load products
336+
let productarray: Product[] = [];
337+
for (var i = 1; i <= productCount; i++) {
338+
const product : Product = await marketContract.methods.products(i).call();
339+
productarray.push(product);
340+
}} else {
341+
window.alert("Marketplace contract not deployed to detected network.");
342+
}}catch (error) {
343+
window.alert("network not detected.");
344+
}};
345+
```
346+
We need 2 functions to call smart contract methods
347+
### CreateProduct
348+
349+
```javascript
350+
const createProduct = async(name: string, price: number) => {
351+
if(!marketplace){
352+
return
353+
}
354+
try {
355+
await marketplace.methods
356+
.createProduct(name, price)
357+
.send({ from: account })
358+
.on("error", function (error: Error) {
359+
window.alert(error.message);
360+
});
361+
await loadBlockchainData()
362+
} catch (error) {
363+
window.alert(error.message);
364+
} };
365+
```
366+
367+
### PurchaseProduct
368+
```javascript
369+
const purchaseProduct = async(id: string, price: number) => {
370+
let idn = Number(id);
371+
if(!marketplace){
372+
return
373+
}
374+
try {
375+
marketplace.methods
376+
.purchaseProduct(idn)
377+
.send({ from: account, value: price })
378+
.on("error", function (error: Error) {
379+
window.alert(error.message);
380+
});
381+
await loadBlockchainData()
382+
} catch (error) {
383+
window.alert(error.message);
384+
}};
385+
```
386+
387+

0 commit comments

Comments
 (0)