├── .gitignore ├── 02-counter-contract ├── src │ ├── index.html │ └── index.ts ├── tsconfig.json ├── hardhat.config.ts ├── contracts │ └── Counter.sol ├── scripts │ └── deploy.ts ├── package.json └── webpack.config.js ├── 01-first-smart-contract ├── src │ ├── index.html │ └── index.ts ├── tsconfig.json ├── hardhat.config.ts ├── contracts │ └── HelloWorld.sol ├── scripts │ └── deploy.ts ├── test │ └── HelloWorld.ts ├── package.json └── webpack.config.js ├── 03-game-contract ├── hardhat.config.ts ├── contracts │ ├── TestHero.sol │ └── Here.sol ├── package.json └── test │ └── Hero.ts ├── 04-diamond-pattern ├── hardhat.config.ts ├── contracts │ ├── AppStorage.sol │ ├── Fallback.sol │ ├── Delegatecall.sol │ └── Storage.sol ├── scripts │ ├── deploy-fallback.ts │ ├── deploy-delegatecall.ts │ └── deploy-storage.ts └── package.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | dist 4 | #Hardhat files 5 | cache 6 | artifacts -------------------------------------------------------------------------------- /02-counter-contract/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /01-first-smart-contract/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /01-first-smart-contract/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "resolveJsonModule": true, 4 | "esModuleInterop": true 5 | } 6 | } -------------------------------------------------------------------------------- /02-counter-contract/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "resolveJsonModule": true, 4 | "esModuleInterop": true 5 | } 6 | } -------------------------------------------------------------------------------- /03-game-contract/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-waffle"; 2 | 3 | /** 4 | * @type import('hardhat/config').HardhatUserConfig 5 | */ 6 | module.exports = { 7 | solidity: "0.8.10", 8 | }; 9 | -------------------------------------------------------------------------------- /01-first-smart-contract/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-waffle"; 2 | /** 3 | * @type import('hardhat/config').HardhatUserConfig 4 | */ 5 | module.exports = { 6 | solidity: "0.8.10", 7 | }; 8 | -------------------------------------------------------------------------------- /04-diamond-pattern/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-waffle"; 2 | 3 | /** 4 | * @type import('hardhat/config').HardhatUserConfig 5 | */ 6 | module.exports = { 7 | solidity: "0.8.10", 8 | }; 9 | -------------------------------------------------------------------------------- /04-diamond-pattern/contracts/AppStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | struct AppStorage { 5 | uint256 a; 6 | uint256 b; 7 | uint8 c; 8 | address ContractC; 9 | } 10 | -------------------------------------------------------------------------------- /02-counter-contract/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-waffle"; 2 | /** 3 | * @type import('hardhat/config').HardhatUserConfig 4 | */ 5 | module.exports = { 6 | solidity: "0.8.10", 7 | networks: { 8 | hardhat: { 9 | chainId: 1337 10 | } 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /01-first-smart-contract/contracts/HelloWorld.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // tell Solidity, what compiler you expect 3 | pragma solidity ^0.8.0; 4 | 5 | contract HelloWorld { 6 | function hello() public pure returns (string memory) { 7 | return "Hello, World"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-game-contract/contracts/TestHero.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "./Here.sol"; 5 | 6 | contract TestHero is Hero { 7 | uint256 random; 8 | 9 | function generateRandom() public view override returns (uint256) { 10 | return random; 11 | } 12 | 13 | function setRandom(uint256 r) public { 14 | random = r; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /01-first-smart-contract/scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-ethers"; 2 | import { ethers } from "hardhat"; 3 | 4 | async function deploy() { 5 | const HelloWorld = await ethers.getContractFactory("HelloWorld"); 6 | const hello = await HelloWorld.deploy(); 7 | await hello.deployed(); 8 | return hello; 9 | } 10 | 11 | // @ts-ignore 12 | async function sayHello(hello) { 13 | console.log("Say Hello: ", await hello.hello()); 14 | } 15 | 16 | deploy().then(sayHello); -------------------------------------------------------------------------------- /02-counter-contract/contracts/Counter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "hardhat/console.sol"; 5 | 6 | contract Counter { 7 | uint256 counter; 8 | 9 | event CounterInc(uint256 counter); 10 | 11 | // write state 12 | function count() public { 13 | counter++; 14 | console.log("Counter : ", counter); 15 | emit CounterInc(counter); 16 | } 17 | 18 | // read state 19 | function getCounter() public view returns (uint256) { 20 | return counter; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /04-diamond-pattern/contracts/Fallback.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | import "hardhat/console.sol"; 4 | 5 | interface IFallback { 6 | function count() external; 7 | } 8 | 9 | contract Fallback { 10 | function foo() internal view { 11 | console.log("Hello, World!"); 12 | } 13 | 14 | // function that catches call that doesn't matches any func invocation 15 | fallback() external payable { 16 | foo(); 17 | console.log("fallback"); 18 | 19 | revert("You shouldn't be here!"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /04-diamond-pattern/scripts/deploy-fallback.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-ethers" 2 | import { Contract } from "ethers"; 3 | import { ethers } from "hardhat"; 4 | 5 | async function deploy() { 6 | const Fallback = await ethers.getContractFactory("Fallback"); 7 | const fallback = await Fallback.deploy(); 8 | await fallback.deployed() 9 | 10 | return fallback; 11 | } 12 | 13 | async function callCount(fallback: Contract) { 14 | const f = await ethers.getContractAt("IFallback", fallback.address); 15 | await f.count(); 16 | } 17 | 18 | deploy().then(callCount); -------------------------------------------------------------------------------- /02-counter-contract/scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-ethers"; 2 | import { ethers } from "hardhat"; 3 | 4 | async function deploy() { 5 | // grab counter 6 | const Counter = await ethers.getContractFactory("Counter"); 7 | // create counter on the network 8 | const counter = await Counter.deploy(); 9 | await counter.deployed(); 10 | 11 | return counter; 12 | } 13 | 14 | // @ts-ignore 15 | async function count(counter) { 16 | await counter.count(); 17 | console.log("Counter from typescript : ", await counter.getCounter()); 18 | } 19 | 20 | deploy().then(count); -------------------------------------------------------------------------------- /04-diamond-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "04-diamond-pattern", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@nomiclabs/hardhat-ethers": "^2.0.5", 14 | "@nomiclabs/hardhat-waffle": "^2.0.3", 15 | "chai": "^4.3.6", 16 | "ethereum-waffle": "^3.4.4", 17 | "ethers": "^5.6.2", 18 | "hardhat": "^2.9.2", 19 | "ts-node": "^10.7.0", 20 | "typescript": "^4.6.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /01-first-smart-contract/test/HelloWorld.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-ethers"; // adds bunch of convenient methods, to ethers library 2 | import { ethers } from "hardhat"; 3 | import { expect } from "chai"; // for testing 4 | 5 | describe("Hello World", () => { 6 | it("should get the hello world",async () => { 7 | // Deploy 8 | // create EVM understandable format 9 | const HelloWorld = await ethers.getContractFactory("HelloWorld"); 10 | // deploy it to a network 11 | const hello = await HelloWorld.deploy(); 12 | // wait 13 | await hello.deployed(); 14 | // Call function to test' 15 | expect(await hello.hello()).to.equal("Hello, World"); 16 | }); 17 | }); -------------------------------------------------------------------------------- /03-game-contract/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "03-game-contract", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@nomiclabs/hardhat-ethers": "^2.0.5", 14 | "@nomiclabs/hardhat-waffle": "^2.0.3", 15 | "@types/chai": "^4.3.0", 16 | "@types/mocha": "^9.1.0", 17 | "@types/node": "^17.0.21", 18 | "chai": "^4.3.6", 19 | "ethereum-waffle": "^4.0.0-alpha.0", 20 | "ethers": "^5.6.0", 21 | "hardhat": "^2.9.1", 22 | "ts-node": "^10.7.0", 23 | "typescript": "^4.6.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /02-counter-contract/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "02-counter-contract", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@nomiclabs/hardhat-ethers": "^2.0.5", 14 | "@nomiclabs/hardhat-waffle": "^2.0.3", 15 | "chai": "^4.3.6", 16 | "dotenv": "^16.0.0", 17 | "ethereum-waffle": "^3.4.0", 18 | "ethers": "^5.6.0", 19 | "hardhat": "^2.9.1", 20 | "html-webpack-plugin": "^5.5.0", 21 | "ts-loader": "^9.2.8", 22 | "ts-node": "^10.7.0", 23 | "typescript": "^4.6.2", 24 | "webpack": "^5.70.0", 25 | "webpack-cli": "^4.9.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /01-first-smart-contract/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "01-first-smart-contract", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@nomiclabs/hardhat-ethers": "^2.0.5", 14 | "@nomiclabs/hardhat-waffle": "^2.0.3", 15 | "@types/chai": "^4.3.0", 16 | "@types/mocha": "^9.1.0", 17 | "@types/node": "^17.0.21", 18 | "chai": "^4.3.6", 19 | "dotenv": "^16.0.0", 20 | "ethereum-waffle": "^3.4.0", 21 | "ethers": "^5.5.4", 22 | "hardhat": "^2.9.1", 23 | "html-webpack-plugin": "^5.5.0", 24 | "ts-loader": "^9.2.7", 25 | "ts-node": "^10.7.0", 26 | "typescript": "^4.6.2", 27 | "webpack": "^5.70.0", 28 | "webpack-cli": "^4.9.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /04-diamond-pattern/contracts/Delegatecall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | import "hardhat/console.sol"; 4 | 5 | // deploy this contract first 6 | contract A { 7 | // storage layout must be the same as Contract B 8 | uint256 a; 9 | 10 | function setA(uint256 _a) public { 11 | a = _a; 12 | } 13 | 14 | function getA() public view returns (uint256) { 15 | return a; 16 | } 17 | } 18 | 19 | contract B { 20 | uint256 b; 21 | address ContractA; 22 | 23 | constructor(address _A) { 24 | ContractA = _A; 25 | } 26 | 27 | function setB(uint256 _b) public { 28 | b = _b; 29 | // B's storage is set, A is not modified. 30 | (bool sucess, bytes memory data) = ContractA.delegatecall( 31 | abi.encodeWithSignature("SetA(uint256)", _b + 1) 32 | ); 33 | } 34 | 35 | function getB() public view returns (uint256) { 36 | return b; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /04-diamond-pattern/contracts/Storage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | import "hardhat/console.sol"; 4 | import "./AppStorage.sol"; 5 | 6 | contract C { 7 | AppStorage s; 8 | 9 | function setA(uint256 _a) public { 10 | s.a = _a; 11 | } 12 | 13 | function getA() public view returns (uint256) { 14 | return s.a; 15 | } 16 | } 17 | 18 | contract D { 19 | AppStorage s; 20 | 21 | constructor(address _C) { 22 | s.ContractC = _C; 23 | s.b = 0x20; 24 | s.c = 0x30; 25 | } 26 | 27 | function setB(uint256 _b) public { 28 | s.b = _b; 29 | // B's storage is set, A is not modified. 30 | (bool sucess, bytes memory data) = s.ContractC.delegatecall( 31 | abi.encodeWithSignature("setA(uint256)", _b + 1) 32 | ); 33 | console.log("Success", sucess); 34 | } 35 | 36 | function getB() public view returns (uint256) { 37 | return s.b; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /04-diamond-pattern/scripts/deploy-delegatecall.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-ethers" 2 | import { ethers } from "hardhat"; 3 | 4 | async function deployContract(name: string, ...args: any) { 5 | const Contract = await ethers.getContractFactory(name); 6 | const contract = await Contract.deploy(...args); 7 | await contract.deployed(); 8 | 9 | return contract; 10 | } 11 | 12 | async function delegateCall() { 13 | const a = await deployContract("A"); 14 | const b = await deployContract("B", a.address); 15 | 16 | console.log("A : ", await a.getA()); 17 | console.log("B : ", await b.getB()); 18 | console.log("--------------------"); 19 | 20 | await a.setA(10); 21 | console.log("A : ", await a.getA()); 22 | console.log("B : ", await b.getB()); 23 | console.log("--------------------"); 24 | 25 | await b.setB(20); 26 | console.log("A : ", await a.getA()); 27 | console.log("B : ", await b.getB()); 28 | console.log("--------------------"); 29 | } 30 | 31 | delegateCall(); -------------------------------------------------------------------------------- /04-diamond-pattern/scripts/deploy-storage.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-ethers"; 2 | import { Contract } from "ethers"; 3 | import { ethers } from "hardhat"; 4 | 5 | async function deployContract(name: string, ...args: any) { 6 | const Contract = await ethers.getContractFactory(name); 7 | const contract = await Contract.deploy(...args); 8 | await contract.deployed(); 9 | 10 | return contract; 11 | } 12 | 13 | async function printStorage(contract: Contract, name: string, slots: any) { 14 | for (let i = 0; i < slots; i++) { 15 | console.log( 16 | name, 17 | i, 18 | await ethers.provider.getStorageAt(contract.address, i) 19 | ); 20 | } 21 | } 22 | 23 | async function storage() { 24 | const c = await deployContract("C"); 25 | const d = await deployContract("D", c.address); 26 | 27 | await printStorage(d, "D", 3); 28 | console.log("------------------"); 29 | await c.setA(0x15); 30 | await printStorage(d, "D", 3); 31 | console.log("------------------"); 32 | await d.setB(0x25); 33 | await printStorage(d, "D", 3); 34 | } 35 | 36 | storage(); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Pervez Shoaib 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /01-first-smart-contract/src/index.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "ethers"; 2 | 3 | function getEth() { 4 | // @ts-ignore 5 | const eth = window.ethereum; 6 | if (!eth) throw new Error("Get MetaMask!!!"); 7 | return eth; 8 | } 9 | 10 | async function hasAccunts() { 11 | const eth = getEth(); 12 | const accounts = await eth.request({methode: "eth_accounts"}) as string[]; 13 | return accounts && accounts.length; 14 | } 15 | 16 | async function requestAccunts() { 17 | const eth = getEth(); 18 | const accounts = await eth.request({methode: "eth_requestAccounts"}) as string[]; 19 | return accounts && accounts.length; 20 | } 21 | 22 | async function getContract() { 23 | const contractAddress = process.env.CONTRACT_ADDRESS; 24 | if (!await hasAccunts() && await requestAccunts()){ 25 | throw new Error("Can't Access MetaMask!!!"); 26 | } 27 | // we should have access to your meta mask at this point 28 | // get provider, which is metamask 29 | const provider = new ethers.providers.Web3Provider(getEth()); 30 | const contract = new ethers.Contract( 31 | contractAddress, // given the location of our contract, in this network 32 | [ 33 | "function hello() public pure returns (string memory)" 34 | ], // give interface, abi 35 | provider, // tells where the networks at 36 | ); 37 | 38 | console.log("We have done it, time to call"); 39 | console.log(await contract.hello()); 40 | } 41 | 42 | getContract(); -------------------------------------------------------------------------------- /02-counter-contract/webpack.config.js: -------------------------------------------------------------------------------- 1 | const dotenv = require("dotenv"); 2 | dotenv.config(); 3 | 4 | const path = require("path"); 5 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 6 | const webpack = require("webpack"); 7 | 8 | module.exports = { 9 | entry: "./src/index.ts", // bundle"s entry point 10 | output: { 11 | path: path.resolve(__dirname, "dist"), // output directory 12 | filename: "[name].js", // name of the generated bundle 13 | }, 14 | resolve: { 15 | extensions: [".js", ".ts", ".json"], 16 | }, 17 | 18 | mode: "development", 19 | 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.ts$/, 24 | loader: "ts-loader", 25 | exclude: /node_modules/, 26 | }, 27 | 28 | { 29 | test: /\.css$/i, 30 | use: ["style-loader", "css-loader"], 31 | }, 32 | ], 33 | }, 34 | plugins: [ 35 | new HtmlWebpackPlugin({ 36 | template: "./src/index.html", 37 | inject: "body", 38 | }), 39 | new webpack.DefinePlugin({ 40 | 'process.env.CONTRACT_ADDRESS': JSON.stringify(process.env.CONTRACT_ADDRESS), 41 | 'process.env.DEBUG': JSON.stringify(process.env.DEBUG), 42 | }), 43 | ], 44 | 45 | watch: true, 46 | 47 | devServer: { 48 | historyApiFallback: true, 49 | contentBase: './', 50 | watchOptions: { 51 | aggregateTimeout: 300, 52 | poll: 1000 53 | } 54 | } 55 | }; -------------------------------------------------------------------------------- /01-first-smart-contract/webpack.config.js: -------------------------------------------------------------------------------- 1 | const dotenv = require("dotenv"); 2 | dotenv.config(); 3 | 4 | const path = require("path"); 5 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 6 | const webpack = require("webpack"); 7 | 8 | module.exports = { 9 | entry: "./src/index.ts", // bundle"s entry point 10 | output: { 11 | path: path.resolve(__dirname, "dist"), // output directory 12 | filename: "[name].js", // name of the generated bundle 13 | }, 14 | resolve: { 15 | extensions: [".js", ".ts", ".json"], 16 | }, 17 | 18 | mode: "development", 19 | 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.ts$/, 24 | loader: "ts-loader", 25 | exclude: /node_modules/, 26 | }, 27 | 28 | { 29 | test: /\.css$/i, 30 | use: ["style-loader", "css-loader"], 31 | }, 32 | ], 33 | }, 34 | plugins: [ 35 | new HtmlWebpackPlugin({ 36 | template: "./src/index.html", 37 | inject: "body", 38 | }), 39 | new webpack.DefinePlugin({ 40 | 'process.env.CONTRACT_ADDRESS': JSON.stringify(process.env.CONTRACT_ADDRESS), 41 | 'process.env.DEBUG': JSON.stringify(process.env.DEBUG), 42 | }), 43 | ], 44 | 45 | watch: true, 46 | 47 | devServer: { 48 | historyApiFallback: true, 49 | contentBase: './', 50 | watchOptions: { 51 | aggregateTimeout: 300, 52 | poll: 1000 53 | } 54 | } 55 | }; -------------------------------------------------------------------------------- /02-counter-contract/src/index.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "ethers"; 2 | import CounterJson from "../artifacts/contracts/Counter.sol/Counter.json"; 3 | 4 | // get ethereum 5 | function getEth() { 6 | // @ts-ignore 7 | const eth = window.ethereum; 8 | if (!eth) throw new Error("Get Metamask!!!"); 9 | return eth; 10 | } 11 | // hasAccoount 12 | async function hasAccounts() { 13 | const eth = getEth(); 14 | const accounts = await eth.request({method: "eth_accounts"}) as String[]; 15 | return accounts.length > 0; 16 | } 17 | // request account 18 | async function requestAccounts() { 19 | const eth = getEth(); 20 | const accounts = await eth.request({method: "eth_requestAccounts"}) as String[]; 21 | return accounts && accounts.length > 0; 22 | } 23 | // getContract 24 | async function getContract() { 25 | const contractAddress = process.env.CONTRACT_ADDRESS; 26 | if (!await hasAccounts() && !await requestAccounts()) { 27 | throw new Error("Can't Access MetaMask!!!"); 28 | } 29 | // get provider 30 | const provider = new ethers.providers.Web3Provider(getEth()); 31 | // get contract 32 | const contract = new ethers.Contract( 33 | contractAddress, 34 | CounterJson.abi, // abi 35 | provider.getSigner() 36 | ); 37 | 38 | const el = document.createElement("div"); 39 | async function setCounter(count?) { 40 | el.innerHTML = count || await contract.getCounter(); 41 | } 42 | setCounter(); 43 | const button = document.createElement("button"); 44 | button.innerText = "increment"; 45 | button.onclick = async function () { 46 | await contract.count(); 47 | } 48 | 49 | // listens to the increment counter 50 | contract.on(contract.filters.CounterInc(), function (count) { 51 | setCounter(count); 52 | }); 53 | 54 | document.body.appendChild(el); 55 | document.body.appendChild(button); 56 | } 57 | 58 | getContract(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ethereum and Smart Contracts with Solidity 2 | 3 | A collection of projects and resources to learn about creating and deploying smart contracts on Ethereum using Solidity. 4 | 5 | It also covers how to communicate with smart contracts from Node.js and browsers, as well as more advanced concepts such as memory layout, delegate calls, and fallback functions. 6 | 7 | Additionally, the repository provides an implementation of the diamond pattern, which enables you to create smart contracts of arbitrary size. 8 | 9 | ## Projects 10 | 11 | ### [01-first-smart-contract](https://github.com/devnova777/ethereum-and-smart-contracts-with-solidity/tree/master/01-first-smart-contract) 12 | 13 | The first Hello World contract with basics of creating and deploying a smart contract on the Ethereum blockchain using Solidity. 14 | 15 | ### [02-counter-contract](https://github.com/devnova777/ethereum-and-smart-contracts-with-solidity/tree/master/02-counter-contract) 16 | 17 | A counter contract that can be incremented and decremented. Unlike the hello world example, this contract changes state. The change in state creates a new block and triggers a gas fee. 18 | 19 | ### [03-game-contract](https://github.com/devnova777/ethereum-and-smart-contracts-with-solidity/tree/master/03-game-contract) 20 | 21 | A basic RPG game, the contract uses the enum type to manage the different player types, uses a mapping to store the heroes generated by the user, uses a random number generator to select a player attribute and assign a value. The payment modifier is added to any function that requires a monetary payment to be included with the function call. 22 | 23 | ### [04-diamond-pattern](https://github.com/devnova777/ethereum-and-smart-contracts-with-solidity/tree/master/04-diamond-pattern) 24 | 25 | The Diamond Pattern helps address contracts that are too large and scenarios where a contract needs to be updated. An external fallback method is created to handle any calls to methods that do not exist. In order for the fallback to work, non-existent methods should be defined in an interface. 26 | -------------------------------------------------------------------------------- /03-game-contract/test/Hero.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-ethers"; 2 | import { ethers } from "hardhat"; 3 | import { expect } from "chai"; 4 | import { Contract } from "ethers"; 5 | 6 | describe("Hero", function() { 7 | async function deployHero() { 8 | const Hero = await ethers.getContractFactory("TestHero"); 9 | const hero = await Hero.deploy(); 10 | await hero.deployed(); 11 | 12 | return hero; 13 | } 14 | let hero : Contract; 15 | // runs once before the first test in this block 16 | before(async function () { 17 | hero = await deployHero(); 18 | }); 19 | it("should get a zero hero array", async function () { 20 | expect(await hero.getHeroes()).to.deep.equal([]); 21 | }) 22 | it("should fail at creating hero cause of payment", async function () { 23 | let e : any; 24 | try { 25 | await hero.createHero(0, { 26 | value: ethers.utils.parseEther("0.0499999999") 27 | }); 28 | } catch (err) { 29 | e = err; 30 | } 31 | 32 | expect(e.message.includes("Please send more money!!!")).to.equal(true); 33 | }) 34 | it("should check if stats are correctly assigned", async function () { 35 | const hero = await deployHero(); 36 | await hero.setRandom(69); 37 | // generate hero 38 | await hero.createHero(0, { 39 | value: ethers.utils.parseEther("0.05") 40 | }); 41 | // get the first hero 42 | const h = (await hero.getHeroes())[0]; 43 | // For 69; [S, H, D, I, M] 44 | // 1st Pos will be 4, Magic; value will be 16; [S, H, D, I] 45 | // 2nd Pos will be 1, Health; value will be 2; [S, I, D] 46 | // 3nd Pos will be 0, Stength; value will be 6; [D, I] 47 | // 4nd Pos will be 1, Intellect; value will be 10; [D] 48 | // 5nd Pos will be 0, Dexterity; value will be 14; [] 49 | expect(await hero.getMagic(h)).to.equal(16); 50 | expect(await hero.getHealth(h)).to.equal(2); 51 | expect(await hero.getStrength(h)).to.equal(6); 52 | expect(await hero.getIntellect(h)).to.equal(10); 53 | expect(await hero.getDexterity(h)).to.equal(14); 54 | }) 55 | }); -------------------------------------------------------------------------------- /03-game-contract/contracts/Here.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract Hero { 5 | enum Class { 6 | Mage, 7 | Healer, 8 | Barbarian 9 | } 10 | 11 | // store heroes 12 | mapping(address => uint256[]) addressToHeroes; 13 | 14 | function getHeroes() public view returns (uint256[] memory) { 15 | return addressToHeroes[msg.sender]; 16 | } 17 | 18 | // random number generation 19 | function generateRandom() public view virtual returns (uint256) { 20 | return 21 | uint256( 22 | keccak256(abi.encodePacked(block.difficulty, block.timestamp)) 23 | ); 24 | } 25 | 26 | function getStrength(uint256 hero) public pure returns (uint32) { 27 | return uint32((hero >> 2) & 0x1F); 28 | } 29 | 30 | function getHealth(uint256 hero) public pure returns (uint32) { 31 | return uint32((hero >> 7) & 0x1F); 32 | } 33 | 34 | function getDexterity(uint256 hero) public pure returns (uint32) { 35 | return uint32((hero >> 12) & 0x1F); 36 | } 37 | 38 | function getIntellect(uint256 hero) public pure returns (uint32) { 39 | return uint32((hero >> 17) & 0x1F); 40 | } 41 | 42 | function getMagic(uint256 hero) public pure returns (uint32) { 43 | return uint32((hero >> 22) & 0x1F); 44 | } 45 | 46 | function createHero(Class class) public payable { 47 | require(msg.value >= 0.05 ether, "Please send more money!!!"); 48 | // strength, health, intellect, magic, dexterity 49 | 50 | uint256[] memory stats = new uint256[](5); 51 | stats[0] = 2; // strength 52 | stats[1] = 7; // health 53 | stats[2] = 12; // dexterity 54 | stats[3] = 17; // intellect 55 | stats[4] = 22; // magic 56 | 57 | uint256 len = 5; 58 | uint256 hero = uint256(class); 59 | 60 | do { 61 | // randomly select a stat from stats array 62 | uint256 pos = generateRandom() % len; 63 | // ramdomly choose a value for that stat 64 | uint256 value = (generateRandom() % (13 + len)) + 1; 65 | // put stats info into hero 66 | // first 2 bits represents class 67 | // next 5 bits represents health 68 | // ...and so on 69 | hero |= value << stats[pos]; 70 | 71 | len--; 72 | stats[pos] = stats[len]; 73 | } while (len > 0); 74 | addressToHeroes[msg.sender].push(hero); 75 | } 76 | } 77 | --------------------------------------------------------------------------------