├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ └── feature-creation.yml └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .gitpod.yml ├── LICENSE ├── README.md ├── apps ├── hardhat-ts │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .mocharc.js │ ├── .prettierignore │ ├── .solhint.json │ ├── .solhintignore │ ├── .vscode │ │ ├── extensions.json │ │ ├── launch.json │ │ └── settings.json │ ├── README.md │ ├── _scripts.js │ ├── deploy │ │ └── 001_deploy_storage.ts │ ├── hardhat.config.ts │ ├── package.json │ ├── scripts │ │ ├── fundingFromCoinbase.ts │ │ ├── seed.ts │ │ └── setMessage.ts │ ├── src │ │ └── Storage │ │ │ └── Storage.sol │ ├── test │ │ ├── Storage.test.ts │ │ ├── chai-setup.ts │ │ └── utils │ │ │ └── index.ts │ ├── tsconfig.json │ └── utils │ │ └── network.ts └── web │ ├── .eslintrc.js │ ├── README.md │ ├── ethereum.d.ts │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── src │ ├── pages │ │ ├── _app.tsx │ │ └── index.tsx │ └── styles │ │ └── global.css │ ├── tailwind.config.js │ └── tsconfig.json ├── gitpod-port.png ├── package.json ├── packages ├── config │ ├── eslint-preset.js │ ├── package.json │ ├── postcss.config.js │ └── tailwind.config.js ├── tsconfig │ ├── README.md │ ├── base.json │ ├── hardhat.json │ ├── nextjs.json │ ├── package.json │ └── react-library.json └── ui │ ├── Button.tsx │ ├── WagmiProvider.tsx │ ├── WalletConnectModal.tsx │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── wallet.png /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | title: "[Bug]: " 4 | labels: ["bug"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this bug report! 10 | - type: textarea 11 | id: what-happened 12 | attributes: 13 | label: What happened? 14 | description: Also tell us, what did you expect to happen? 15 | placeholder: Tell us what you see! 16 | value: "A bug happened!" 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: repo-steps 21 | attributes: 22 | label: How to reproduce the bug? 23 | description: Please explain how to reproduce the bug 24 | placeholder: Tell us how to repo the bug! 25 | value: | 26 | 1. Go to '...' 27 | 2. Click on '...' 28 | 3. Scroll down to '...' 29 | 4. See error 30 | validations: 31 | required: true 32 | - type: "checkboxes" 33 | id: browsers 34 | attributes: 35 | label: What browsers are you seeing the problem on? 36 | options: 37 | - label: Firefox 38 | - label: Chrome 39 | - label: Safari 40 | - label: Microsoft Edge 41 | - type: checkboxes 42 | id: devices 43 | attributes: 44 | label: What device type(s) did you see the problem on? 45 | options: 46 | - label: "Desktop" 47 | - label: "Mobile" 48 | - label: "Tablet" 49 | - type: checkboxes 50 | id: operating-system 51 | attributes: 52 | label: "What operating system(s) did you see the problem on?" 53 | options: 54 | - label: "macOS" 55 | - label: "Windows" 56 | - label: "Linux" 57 | - type: textarea 58 | id: additional 59 | attributes: 60 | label: Additional information 61 | description: Use this section to provide any additional information you may have like screenshots, logs, notes, video links, etc... 62 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-creation.yml: -------------------------------------------------------------------------------- 1 | name: "Feature request" 2 | description: "File a Feature request" 3 | title: "[Feature]: " 4 | labels: ["enhancement"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out a feature request! 10 | - type: textarea 11 | id: context 12 | attributes: 13 | label: Context / Background / How This Idea Came To Fruition 14 | description: Please provide some context around your idea 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: problem 19 | attributes: 20 | label: Problem / Opportunity 21 | description: What are we trying to solve/improve with this feature? 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: solution 26 | attributes: 27 | label: Proposed Solution / How It Can Be Improved 28 | description: What will your feature / enhancement look like? (i.e frontend changes should have accompanying screenshots around the solution) 29 | validations: 30 | required: true 31 | - type: textarea 32 | id: testing 33 | attributes: 34 | label: Testing the changes 35 | description: Please provide a high level description of how you'll verify your changes work and won't break existing functionality? 36 | validations: 37 | required: true 38 | - type: textarea 39 | id: additional 40 | attributes: 41 | label: Additional Information 42 | description: Please provide any additional information around your request here 43 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What does it do? 2 | 3 | ### Any helpful background information? 4 | 5 | ### Any new dependencies? Why were they added? 6 | 7 | ### Relevant screenshots/gifs 8 | 9 | ### Does it close any issues? 10 | 11 | Closes #... -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | # next.js 12 | .next/ 13 | out/ 14 | build 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | .pnpm-debug.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | 32 | # turbo 33 | .turbo 34 | 35 | # hardhat 36 | typechain -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: pnpm install 3 | command: pnpm dev 4 | ports: 5 | - port: 3000 6 | visibility: public 7 | - port: 8545 8 | visibility: public 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nafees Nazik 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # full-stack dApp starter for solidity smart contract development 2 | 3 | A monorepo boilerplate code for typesafe full-stack [Solidity](https://soliditylang.org/) development. 4 | 5 | ## Contents 6 | - [Prerequisites](#prerequisites) 7 | - [Features](#features) 8 | - [Run in Gitpod](#run-in-gitpod) 9 | - [Run Locally](#run-locally) 10 | - [Build For Production](#build-for-production) 11 | - [Acknowledgements](#acknowledgements) 12 | - [License](#license) 13 | 14 | ## Prerequisites 15 | 16 | - [pnpm](https://pnpm.io/) 17 | - [Node.js](https://nodejs.org/en/download/) 18 | - [MetaMask wallet browser extension](https://metamask.io/download.html). 19 | 20 | ## Features 21 | 22 | Here's an overview of the included frameworks and tools. 23 | 24 | - **Next.js** - Minimalistic framework for server-rendered React applications. 25 | - **Typescript** - Superset of JavaScript which primarily provides optional static typing, classes and interfaces. 26 | - **ESLint** - The pluggable linting utility. 27 | - **Turborepo** - High-performance build system for JavaScript and TypeScript codebases. 28 | - **PNPM** - Fast, disk space efficient package manager. 29 | - **Wagmi** - React Hooks library for Ethereum. 30 | - **Tailwind CSS** - Rapidly build modern websites without ever leaving your HTML. 31 | - **Typechain** - TypeScript bindings for Ethereum smart contracts. 32 | - **Hardhat** - Ethereum development environment for professionals. 33 | - **Hardhat-deploy** - A Hardhat Plugin For Replicable Deployments And Easy Testing. 34 | - **Chai** - A BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework. 35 | - **Mocha** - A feature-rich JavaScript test framework. 36 | 37 | ## Run in Gitpod 38 | To run this project in Gitpod, follow these steps: 39 |
40 | 1. Click this link to deploy to gitpod 41 |
42 | 43 | [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#github.com/G3root/nextjs-dapp-starter-ts) 44 | 45 | 2. Import the RPC address given to you by GitPod into your MetaMask wallet 46 | 47 | This endpoint will look something like this: 48 | 49 | ```bash 50 | https://8545-copper-swordtail-j1mvhxv3.ws-eu18.gitpod.io/ 51 | ``` 52 | 53 | The chain ID should be 1337. If you have a localhost rpc set up, you may need to overwrite it. 54 |
55 | 56 | ![MetaMask RPC Import](wallet.png) 57 | 58 | you can also change your status of your open ports by going to port settings. 59 |
60 | 61 | ![port settings](gitpod-port.png) 62 | 63 | ## Run Locally 64 | 65 | Clone the project 66 | 67 | ```bash 68 | npx degit G3root/nextjs-dapp-starter-ts my-project 69 | ``` 70 | 71 | Go to the project directory 72 | 73 | ```bash 74 | cd my-project 75 | ``` 76 | 77 | Install dependencies 78 | 79 | ```bash 80 | pnpm install 81 | ``` 82 | 83 | Start the development server 84 | 85 | ```bash 86 | pnpm dev 87 | ``` 88 | 89 | ## Build For Production 90 | 91 | To generate production build 92 | 93 | ```bash 94 | pnpm build 95 | ``` 96 | ## Acknowledgements 97 | 98 | - [template-ethereum-contracts](https://github.com/wighawag/template-ethereum-contracts) 99 | 100 | ## License 101 | 102 | [MIT](https://choosealicense.com/licenses/mit/) -------------------------------------------------------------------------------- /apps/hardhat-ts/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | 11 | [*.sol] 12 | indent_size = 4 -------------------------------------------------------------------------------- /apps/hardhat-ts/.eslintignore: -------------------------------------------------------------------------------- 1 | export/ 2 | deployments/ 3 | artifacts/ 4 | cache/ 5 | coverage/ 6 | node_modules/ 7 | package.json 8 | typechain/ -------------------------------------------------------------------------------- /apps/hardhat-ts/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | parserOptions: { 5 | ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features 6 | sourceType: 'module', // Allows for the use of imports 7 | }, 8 | env: { 9 | commonjs: true, 10 | }, 11 | plugins: ['@typescript-eslint'], 12 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 13 | rules: { 14 | 'no-empty': 'off', 15 | 'no-empty-function': 'off', 16 | '@typescript-eslint/no-empty-function': 'off', 17 | }, 18 | }; -------------------------------------------------------------------------------- /apps/hardhat-ts/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | 7 | #Hardhat files 8 | cache 9 | artifacts 10 | 11 | .yalc 12 | yalc.lock 13 | 14 | contractsInfo.json 15 | deployments/hardhat 16 | deployments/localhost 17 | 18 | .dapp/ 19 | _lib/ -------------------------------------------------------------------------------- /apps/hardhat-ts/.mocharc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | process.env.TS_NODE_FILES = true; 3 | module.exports = { 4 | 'allow-uncaught': true, 5 | diff: true, 6 | extension: ['ts'], 7 | recursive: true, 8 | reporter: 'spec', 9 | require: ['ts-node/register', 'hardhat/register'], // ['ts-node/register/transpile-only'], (for yarn link ) 10 | slow: 300, 11 | spec: 'test/**/*.test.ts', 12 | timeout: 20000, 13 | ui: 'bdd', 14 | watch: false, 15 | 'watch-files': ['src/**/*.sol', 'test/**/*.ts'], 16 | }; -------------------------------------------------------------------------------- /apps/hardhat-ts/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | artifacts 3 | cache 4 | coverage* 5 | gasReporterOutput.json 6 | -------------------------------------------------------------------------------- /apps/hardhat-ts/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": [ 4 | "prettier" 5 | ], 6 | "rules": { 7 | "prettier/prettier": [ 8 | "error", 9 | { 10 | "endOfLine": "auto" 11 | } 12 | ], 13 | "code-complexity": [ 14 | "error", 15 | 7 16 | ], 17 | "compiler-version": [ 18 | "error", 19 | "^0.8.9" 20 | ], 21 | "const-name-snakecase": "off", 22 | "func-name-mixedcase": "off", 23 | "constructor-syntax": "error", 24 | "func-visibility": [ 25 | "error", 26 | { 27 | "ignoreConstructors": true 28 | } 29 | ], 30 | "not-rely-on-time": "off", 31 | "reason-string": [ 32 | "warn", 33 | { 34 | "maxLength": 64 35 | } 36 | ] 37 | } 38 | } -------------------------------------------------------------------------------- /apps/hardhat-ts/.solhintignore: -------------------------------------------------------------------------------- 1 | export/ 2 | deployments/ 3 | artifacts/ 4 | cache/ 5 | coverage/ 6 | node_modules/ 7 | -------------------------------------------------------------------------------- /apps/hardhat-ts/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "editorconfig.editorconfig", 5 | "esbenp.prettier-vscode", 6 | "hbenl.vscode-mocha-test-adapter", 7 | "juanblanco.solidity" 8 | ], 9 | } 10 | -------------------------------------------------------------------------------- /apps/hardhat-ts/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "hardhat node", 8 | "skipFiles": ["/**"], 9 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/hardhat", 10 | "args": ["node"], 11 | "cwd": "${workspaceFolder}" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /apps/hardhat-ts/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.defaultFormatter": "esbenp.prettier-vscode", 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll": true 6 | }, 7 | "solidity.linter": "solhint", 8 | "solidity.defaultCompiler": "remote", 9 | "solidity.compileUsingRemoteVersion": "v0.8.9+commit.e5eed63a", 10 | "solidity.packageDefaultDependenciesContractsDirectory": "", 11 | "solidity.enabledAsYouTypeCompilationErrorCheck": true, 12 | "solidity.validationDelay": 1500, 13 | "solidity.packageDefaultDependenciesDirectory": "node_modules", 14 | "mochaExplorer.env": { 15 | "HARDHAT_CONFIG": "hardhat.config.ts", 16 | "HARDHAT_COMPILE": "true" 17 | }, 18 | "mochaExplorer.require": ["ts-node/register/transpile-only"] 19 | } 20 | -------------------------------------------------------------------------------- /apps/hardhat-ts/README.md: -------------------------------------------------------------------------------- 1 | # Boilerplate for ethereum solidity smart contract development 2 | 3 | ## INSTALL 4 | 5 | ```bash 6 | pnpm install 7 | ``` 8 | 9 | ## TEST 10 | 11 | 12 | - test using hardhat that can leverage hardhat-deploy to reuse deployment procedures and named accounts: 13 | 14 | ```bash 15 | pnpm test 16 | ``` 17 | 18 | 19 | ## SCRIPTS 20 | 21 | Here is the list of npm scripts you can execute: 22 | 23 | Some of them relies on [./\_scripts.js](./_scripts.js) to allow parameterizing it via command line argument (have a look inside if you need modifications) 24 |

25 | 26 | `pnpm prepare` 27 | 28 | As a standard lifecycle npm script, it is executed automatically upon install. It generate config file and typechain to get you started with type safe contract interactions 29 |

30 | 31 | `pnpm lint` and `pnpm lint:fix` 32 | 33 | These commands will lint your code. the `:fix` version will modifiy the files to match the requirement specified in `.eslintrc`. 34 |

35 | 36 | `pnpm compile` 37 | 38 | These will compile your contracts 39 |

40 | 41 | `pnpm void:deploy` 42 | 43 | This will deploy your contracts on the in-memory hardhat network and exit, leaving no trace. quick way to ensure deployments work as intended without consequences 44 |

45 | 46 | `pnpm test [mocha args...]` 47 | 48 | These will execute your tests using mocha. you can pass extra arguments to mocha 49 |

50 | 51 | `pnpm coverage` 52 | 53 | These will produce a coverage report in the `coverage/` folder 54 |

55 | 56 | `pnpm gas` 57 | 58 | These will produce a gas report for function used in the tests 59 |

60 | 61 | `pnpm dev` 62 | 63 | These will run a local hardhat network on `localhost:8545` and deploy your contracts on it. Plus it will watch for any changes and redeploy them. 64 |

65 | 66 | `pnpm local:dev` 67 | 68 | This assumes a local node it running on `localhost:8545`. It will deploy your contracts on it. Plus it will watch for any changes and redeploy them. 69 |

70 | 71 | `pnpm execute [args...]` 72 | 73 | This will execute the script `` against the specified network 74 |

75 | 76 | `pnpm deploy [args...]` 77 | 78 | This will deploy the contract on the specified network. 79 | 80 | Behind the scene it uses `hardhat deploy` command so you can append any argument for it 81 |

82 | 83 | `pnpm export ` 84 | 85 | This will export the abi+address of deployed contract to `` 86 |

87 | 88 | `pnpm fork:execute [--blockNumber ] [--deploy] [args...]` 89 | 90 | This will execute the script `` against a temporary fork of the specified network 91 | 92 | if `--deploy` is used, deploy scripts will be executed 93 |

94 | 95 | `pnpm fork:deploy [--blockNumber ] [args...]` 96 | 97 | This will deploy the contract against a temporary fork of the specified network. 98 | 99 | Behind the scene it uses `hardhat deploy` command so you can append any argument for it 100 |

101 | 102 | `pnpm fork:test [--blockNumber ] [mocha args...]` 103 | 104 | This will test the contract against a temporary fork of the specified network. 105 |

106 | 107 | `pnpm fork:dev [--blockNumber ] [args...]` 108 | 109 | This will deploy the contract against a fork of the specified network and it will keep running as a node. 110 | 111 | Behind the scene it uses `hardhat node` command so you can append any argument for it 112 | -------------------------------------------------------------------------------- /apps/hardhat-ts/_scripts.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | /* eslint-disable no-undef */ 4 | /* eslint-disable @typescript-eslint/no-var-requires */ 5 | const {spawn} = require('child_process'); 6 | const path = require('path'); 7 | require('dotenv').config(); 8 | 9 | const commandlineArgs = process.argv.slice(2); 10 | 11 | function parseArgs(rawArgs, numFixedArgs, expectedOptions) { 12 | const fixedArgs = []; 13 | const options = {}; 14 | const extra = []; 15 | const alreadyCounted = {}; 16 | for (let i = 0; i < rawArgs.length; i++) { 17 | const rawArg = rawArgs[i]; 18 | if (rawArg.startsWith('--')) { 19 | const optionName = rawArg.slice(2); 20 | const optionDetected = expectedOptions[optionName]; 21 | if (!alreadyCounted[optionName] && optionDetected) { 22 | alreadyCounted[optionName] = true; 23 | if (optionDetected === 'boolean') { 24 | options[optionName] = true; 25 | } else { 26 | i++; 27 | options[optionName] = rawArgs[i]; 28 | } 29 | } else { 30 | if (fixedArgs.length < numFixedArgs) { 31 | throw new Error(`expected ${numFixedArgs} fixed args, got only ${fixedArgs.length}`); 32 | } else { 33 | extra.push(rawArg); 34 | } 35 | } 36 | } else { 37 | if (fixedArgs.length < numFixedArgs) { 38 | fixedArgs.push(rawArg); 39 | } else { 40 | for (const opt of Object.keys(expectedOptions)) { 41 | alreadyCounted[opt] = true; 42 | } 43 | extra.push(rawArg); 44 | } 45 | } 46 | } 47 | return {options, extra, fixedArgs}; 48 | } 49 | 50 | function execute(command) { 51 | return new Promise((resolve, reject) => { 52 | const onExit = (error) => { 53 | if (error) { 54 | return reject(error); 55 | } 56 | resolve(); 57 | }; 58 | spawn(command.split(' ')[0], command.split(' ').slice(1), { 59 | stdio: 'inherit', 60 | shell: true, 61 | }).on('exit', onExit); 62 | }); 63 | } 64 | 65 | async function performAction(rawArgs) { 66 | const firstArg = rawArgs[0]; 67 | const args = rawArgs.slice(1); 68 | if (firstArg === 'run') { 69 | const {fixedArgs, extra} = parseArgs(args, 2, {}); 70 | let filepath = fixedArgs[1]; 71 | const folder = path.basename(__dirname); 72 | if (filepath.startsWith(folder + '/') || filepath.startsWith(folder + '\\')) { 73 | filepath = filepath.slice(folder.length + 1); 74 | } 75 | await execute( 76 | `cross-env HARDHAT_DEPLOY_LOG=true HARDHAT_NETWORK=${fixedArgs[0]} ts-node --files ${filepath} ${extra.join(' ')}` 77 | ); 78 | } else if (firstArg === 'deploy') { 79 | const {fixedArgs, extra} = parseArgs(args, 1, {}); 80 | await execute(`hardhat --network ${fixedArgs[0]} deploy ${extra.join(' ')}`); 81 | } else if (firstArg === 'export') { 82 | const {fixedArgs} = parseArgs(args, 2, {}); 83 | await execute(`hardhat --network ${fixedArgs[0]} export --export ${fixedArgs[1]}`); 84 | } else if (firstArg === 'fork:run') { 85 | const {fixedArgs, options, extra} = parseArgs(args, 2, { 86 | deploy: 'boolean', 87 | blockNumber: 'string', 88 | 'no-impersonation': 'boolean', 89 | }); 90 | let filepath = fixedArgs[1]; 91 | const folder = path.basename(__dirname); 92 | if (filepath.startsWith(folder + '/') || filepath.startsWith(folder + '\\')) { 93 | filepath = filepath.slice(folder.length + 1); 94 | } 95 | await execute( 96 | `cross-env ${options.deploy ? 'HARDHAT_DEPLOY_FIXTURE=true' : ''} HARDHAT_DEPLOY_LOG=true HARDHAT_FORK=${ 97 | fixedArgs[0] 98 | } ${options.blockNumber ? `HARDHAT_FORK_NUMBER=${options.blockNumber}` : ''} ${ 99 | options['no-impersonation'] ? `HARDHAT_DEPLOY_NO_IMPERSONATION=true` : '' 100 | } ts-node --files ${filepath} ${extra.join(' ')}` 101 | ); 102 | } else if (firstArg === 'fork:deploy') { 103 | const {fixedArgs, options, extra} = parseArgs(args, 1, { 104 | blockNumber: 'string', 105 | 'no-impersonation': 'boolean', 106 | }); 107 | await execute( 108 | `cross-env HARDHAT_FORK=${fixedArgs[0]} ${ 109 | options.blockNumber ? `HARDHAT_FORK_NUMBER=${options.blockNumber}` : '' 110 | } ${options['no-impersonation'] ? `HARDHAT_DEPLOY_NO_IMPERSONATION=true` : ''} hardhat deploy ${extra.join(' ')}` 111 | ); 112 | } else if (firstArg === 'fork:node') { 113 | const {fixedArgs, options, extra} = parseArgs(args, 1, { 114 | blockNumber: 'string', 115 | 'no-impersonation': 'boolean', 116 | }); 117 | await execute( 118 | `cross-env HARDHAT_FORK=${fixedArgs[0]} ${ 119 | options.blockNumber ? `HARDHAT_FORK_NUMBER=${options.blockNumber}` : '' 120 | } ${ 121 | options['no-impersonation'] ? `HARDHAT_DEPLOY_NO_IMPERSONATION=true` : '' 122 | } hardhat node --hostname 0.0.0.0 ${extra.join(' ')}` 123 | ); 124 | } else if (firstArg === 'fork:test') { 125 | const {fixedArgs, options, extra} = parseArgs(args, 1, { 126 | blockNumber: 'string', 127 | 'no-impersonation': 'boolean', 128 | }); 129 | await execute( 130 | `cross-env HARDHAT_FORK=${fixedArgs[0]} ${ 131 | options.blockNumber ? `HARDHAT_FORK_NUMBER=${options.blockNumber}` : '' 132 | } ${ 133 | options['no-impersonation'] ? `HARDHAT_DEPLOY_NO_IMPERSONATION=true` : '' 134 | } HARDHAT_DEPLOY_FIXTURE=true HARDHAT_COMPILE=true mocha --bail --recursive test ${extra.join(' ')}` 135 | ); 136 | } else if (firstArg === 'fork:dev') { 137 | const {fixedArgs, options, extra} = parseArgs(args, 1, { 138 | blockNumber: 'string', 139 | 'no-impersonation': 'boolean', 140 | }); 141 | await execute( 142 | `cross-env HARDHAT_FORK=${fixedArgs[0]} ${ 143 | options.blockNumber ? `HARDHAT_FORK_NUMBER=${options.blockNumber}` : '' 144 | } ${ 145 | options['no-impersonation'] ? `HARDHAT_DEPLOY_NO_IMPERSONATION=true` : '' 146 | } hardhat node --hostname 0.0.0.0 --watch --export contractsInfo.json ${extra.join(' ')}` 147 | ); 148 | } 149 | } 150 | 151 | performAction(commandlineArgs); 152 | -------------------------------------------------------------------------------- /apps/hardhat-ts/deploy/001_deploy_storage.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from "hardhat/types"; 2 | import { DeployFunction } from "hardhat-deploy/types"; 3 | 4 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 5 | const { deployments, getNamedAccounts } = hre; 6 | const { deploy } = deployments; 7 | 8 | const { deployer } = await getNamedAccounts(); 9 | 10 | await deploy("Storage", { 11 | from: deployer, 12 | log: true, 13 | autoMine: true, // speed up deployment on local network (ganache, hardhat), no effect on live networks 14 | }); 15 | }; 16 | export default func; 17 | func.tags = ["Storage"]; 18 | -------------------------------------------------------------------------------- /apps/hardhat-ts/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import { HardhatUserConfig } from "hardhat/types"; 3 | import "hardhat-deploy"; 4 | import "@nomiclabs/hardhat-ethers"; 5 | import "hardhat-gas-reporter"; 6 | import "@typechain/hardhat"; 7 | import "solidity-coverage"; 8 | import { node_url, accounts, addForkConfiguration } from "./utils/network"; 9 | 10 | const config: HardhatUserConfig = { 11 | solidity: { 12 | compilers: [ 13 | { 14 | version: "0.8.9", 15 | settings: { 16 | optimizer: { 17 | enabled: true, 18 | runs: 2000, 19 | }, 20 | }, 21 | }, 22 | ], 23 | }, 24 | namedAccounts: { 25 | deployer: 0, 26 | }, 27 | networks: addForkConfiguration({ 28 | hardhat: { 29 | initialBaseFeePerGas: 0, // to fix : https://github.com/sc-forks/solidity-coverage/issues/652, see https://github.com/sc-forks/solidity-coverage/issues/652#issuecomment-896330136 30 | chainId: 1337, 31 | }, 32 | localhost: { 33 | url: node_url("localhost"), 34 | accounts: accounts(), 35 | }, 36 | staging: { 37 | url: node_url("rinkeby"), 38 | accounts: accounts("rinkeby"), 39 | }, 40 | production: { 41 | url: node_url("mainnet"), 42 | accounts: accounts("mainnet"), 43 | }, 44 | mainnet: { 45 | url: node_url("mainnet"), 46 | accounts: accounts("mainnet"), 47 | }, 48 | rinkeby: { 49 | url: node_url("rinkeby"), 50 | accounts: accounts("rinkeby"), 51 | }, 52 | kovan: { 53 | url: node_url("kovan"), 54 | accounts: accounts("kovan"), 55 | }, 56 | goerli: { 57 | url: node_url("goerli"), 58 | accounts: accounts("goerli"), 59 | }, 60 | }), 61 | 62 | paths: { 63 | sources: "src", 64 | }, 65 | gasReporter: { 66 | currency: "USD", 67 | gasPrice: 100, 68 | enabled: process.env.REPORT_GAS ? true : false, 69 | coinmarketcap: process.env.COINMARKETCAP_API_KEY, 70 | maxMethodDiff: 10, 71 | }, 72 | typechain: { 73 | outDir: "../web/src/typechain", 74 | target: "ethers-v5", 75 | }, 76 | mocha: { 77 | timeout: 0, 78 | }, 79 | external: process.env.HARDHAT_FORK 80 | ? { 81 | deployments: { 82 | // process.env.HARDHAT_FORK will specify the network that the fork is made from. 83 | // these lines allow it to fetch the deployments from the network being forked from both for node and deploy task 84 | hardhat: ["deployments/" + process.env.HARDHAT_FORK], 85 | localhost: ["deployments/" + process.env.HARDHAT_FORK], 86 | }, 87 | } 88 | : undefined, 89 | }; 90 | 91 | export default config; 92 | -------------------------------------------------------------------------------- /apps/hardhat-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hardhat-ts", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "prepare": "hardhat typechain", 7 | "lint": "eslint \"**/*.{js,ts}\" && solhint src/**/*.sol", 8 | "lint:fix": "eslint --fix \"**/*.{js,ts}\" && solhint --fix src/**/*.sol", 9 | "build": "hardhat clean & hardhat compile", 10 | "compile": "hardhat compile", 11 | "void:deploy": "hardhat deploy", 12 | "test": "cross-env HARDHAT_DEPLOY_FIXTURE=true HARDHAT_COMPILE=true mocha --bail --recursive test", 13 | "gas": "cross-env REPORT_GAS=true hardhat test", 14 | "coverage": "cross-env HARDHAT_DEPLOY_FIXTURE=true hardhat coverage", 15 | "dev:zero": "cross-env MINING_INTERVAL=\"3000,5000\" hardhat node --hostname 0.0.0.0 --no-deploy", 16 | "dev": "cross-env MINING_INTERVAL=\"3000,5000\" hardhat node --hostname 0.0.0.0 --watch", 17 | "local:dev": "hardhat --network localhost deploy --watch", 18 | "execute": "node ./_scripts.js run", 19 | "deploy": "node ./_scripts.js deploy", 20 | "export": "node ./_scripts.js export", 21 | "fork:execute": "node ./_scripts.js fork:run", 22 | "fork:deploy": "node ./_scripts.js fork:deploy", 23 | "fork:dev": "node ./_scripts.js fork:dev", 24 | "fork:node": "node ./_scripts.js fork:node", 25 | "fork:test": "node ./_scripts.js fork:test" 26 | }, 27 | "devDependencies": { 28 | "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers@0.3.0-beta.12", 29 | "@openzeppelin/contracts": "^4.4.1", 30 | "@typechain/ethers-v5": "^8.0.5", 31 | "@typechain/hardhat": "^3.1.0", 32 | "@types/chai": "^4.3.0", 33 | "@types/mocha": "^9.0.0", 34 | "@types/node": "^17.0.5", 35 | "@typescript-eslint/eslint-plugin": "^5.8.0", 36 | "@typescript-eslint/parser": "^5.8.0", 37 | "chai": "^4.2.0", 38 | "chai-ethers": "^0.0.1", 39 | "cross-env": "^7.0.3", 40 | "dotenv": "^10.0.0", 41 | "eslint": "^8.5.0", 42 | "eslint-config-prettier": "^8.3.0", 43 | "ethers": "^5.5.2", 44 | "hardhat": "^2.8.0", 45 | "hardhat-deploy": "^0.9.14", 46 | "hardhat-gas-reporter": "^1.0.4", 47 | "mocha": "^9.1.3", 48 | "prettier": "^2.5.1", 49 | "prettier-plugin-solidity": "^1.0.0-beta.19", 50 | "solhint": "^3.3.6", 51 | "solhint-plugin-prettier": "^0.0.5", 52 | "solidity-coverage": "^0.7.17", 53 | "ts-generator": "^0.1.1", 54 | "ts-node": "^10.4.0", 55 | "typechain": "^6.0.5", 56 | "typescript": "^4.5.4", 57 | "tsconfig": "workspace:*" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /apps/hardhat-ts/scripts/fundingFromCoinbase.ts: -------------------------------------------------------------------------------- 1 | // script used to fund account from a geth coinbase account (geth --dev) 2 | import { ethers, network } from "hardhat"; 3 | import { BigNumber, providers } from "ethers"; 4 | 5 | const { JsonRpcProvider } = providers; 6 | 7 | function wait(numSec: number): Promise { 8 | return new Promise((resolve) => { 9 | setTimeout(resolve, numSec * 1000); 10 | }); 11 | } 12 | 13 | async function main() { 14 | console.log("funding from coinbase ..."); 15 | let found; 16 | while (!found) { 17 | try { 18 | await ethers.provider.send("eth_chainId", []); 19 | found = true; 20 | } catch (e) {} // TODO timeout ? 21 | if (!found) { 22 | console.log(`retrying...`); 23 | await wait(1); 24 | } 25 | } 26 | 27 | if (!("url" in network.config)) { 28 | console.log("cannot run on in memory hardhat network."); 29 | return; 30 | } 31 | 32 | const coinbase = await ethers.provider.send("eth_coinbase", []); 33 | if (!coinbase) { 34 | console.log("no coinbase"); 35 | return; 36 | } 37 | const accounts = await ethers.provider.listAccounts(); 38 | let accountsToFund = accounts; 39 | if (coinbase === accounts[0]) { 40 | accountsToFund = accounts.slice(1); 41 | } 42 | 43 | const coinbaseBalance = await ethers.provider.getBalance(coinbase); 44 | const nonce = await ethers.provider.getTransactionCount(coinbase); 45 | const maxAmount = BigNumber.from("10000000000000000000"); 46 | let amount = coinbaseBalance.div(accountsToFund.length); 47 | if (amount.gt(maxAmount)) { 48 | amount = maxAmount; 49 | } 50 | 51 | if (coinbaseBalance.gt(0)) { 52 | const rawProvider = new JsonRpcProvider(network.config.url); 53 | const coinbaseSigner = rawProvider.getSigner(coinbase); 54 | for (let i = 0; i < accountsToFund.length; i++) { 55 | const tx = await coinbaseSigner.sendTransaction({ 56 | to: accountsToFund[i], 57 | value: amount.sub(21000).toHexString(), 58 | nonce: BigNumber.from(nonce + i).toHexString(), 59 | }); 60 | console.log(tx.hash); 61 | } 62 | } else { 63 | console.log("coinbase has zero balance"); 64 | } 65 | } 66 | 67 | main() 68 | .then(() => process.exit(0)) 69 | .catch((error) => { 70 | console.error(error); 71 | process.exit(1); 72 | }); 73 | -------------------------------------------------------------------------------- /apps/hardhat-ts/scripts/seed.ts: -------------------------------------------------------------------------------- 1 | import { getUnnamedAccounts, ethers } from "hardhat"; 2 | 3 | const messages = [ 4 | "Hello", 5 | "你好", 6 | "سلام", 7 | "здравствуйте", 8 | "Habari", 9 | "Bonjour", 10 | "नमस्ते", 11 | ]; 12 | 13 | async function main() { 14 | const others = await getUnnamedAccounts(); 15 | for (let i = 0; i < messages.length; i++) { 16 | const sender = others[i]; 17 | if (sender) { 18 | const greetingsRegistryContract = await ethers.getContract( 19 | "GreetingsRegistry", 20 | sender 21 | ); 22 | const tx = await greetingsRegistryContract.setMessage(messages[i]); 23 | console.log(tx.hash); 24 | await tx.wait(); 25 | } 26 | } 27 | } 28 | 29 | main() 30 | .then(() => process.exit(0)) 31 | .catch((error) => { 32 | console.error(error); 33 | process.exit(1); 34 | }); 35 | -------------------------------------------------------------------------------- /apps/hardhat-ts/scripts/setMessage.ts: -------------------------------------------------------------------------------- 1 | import { deployments, getUnnamedAccounts } from "hardhat"; 2 | const { execute } = deployments; 3 | // example script 4 | 5 | const args = process.argv.slice(2); 6 | const account = args[0]; 7 | const message = args[1]; 8 | 9 | async function main() { 10 | const accountAddress = isNaN(parseInt(account)) 11 | ? account 12 | : (await getUnnamedAccounts())[parseInt(account)]; 13 | 14 | await execute( 15 | "GreetingsRegistry", 16 | { from: accountAddress, log: true }, 17 | "setMessage", 18 | message || "hello" 19 | ); 20 | } 21 | 22 | main() 23 | .then(() => process.exit(0)) 24 | .catch((error) => { 25 | console.error(error); 26 | process.exit(1); 27 | }); 28 | -------------------------------------------------------------------------------- /apps/hardhat-ts/src/Storage/Storage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | 4 | contract Storage { 5 | string public store; 6 | 7 | function set(string memory string_) public { 8 | store = string_; 9 | } 10 | 11 | function retrieve() public view returns (string memory) { 12 | return store; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/hardhat-ts/test/Storage.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "./chai-setup"; 2 | import { ethers, deployments, getUnnamedAccounts } from "hardhat"; 3 | import { Storage } from "../../web/src/typechain"; 4 | import { setupUsers } from "./utils"; 5 | 6 | const setup = deployments.createFixture(async () => { 7 | await deployments.fixture("Storage"); 8 | const contracts = { 9 | Storage: await ethers.getContract("Storage"), 10 | }; 11 | const users = await setupUsers(await getUnnamedAccounts(), contracts); 12 | return { 13 | ...contracts, 14 | users, 15 | }; 16 | }); 17 | describe("Storage", function () { 18 | it("should set data to the store", async function () { 19 | const { users } = await setup(); 20 | const testMessage = "Hello World"; 21 | await users[0].Storage.set(testMessage); 22 | const currentValue = await users[0].Storage.store(); 23 | expect(currentValue).to.be.not.undefined; 24 | expect(currentValue).to.be.not.null; 25 | expect(currentValue).to.be.not.NaN; 26 | expect(currentValue).to.equal(testMessage); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /apps/hardhat-ts/test/chai-setup.ts: -------------------------------------------------------------------------------- 1 | import chaiModule from "chai"; 2 | import { chaiEthers } from "chai-ethers"; 3 | chaiModule.use(chaiEthers); 4 | export = chaiModule; 5 | -------------------------------------------------------------------------------- /apps/hardhat-ts/test/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { Contract } from "ethers"; 2 | import { ethers } from "hardhat"; 3 | 4 | export async function setupUsers< 5 | T extends { [contractName: string]: Contract } 6 | >(addresses: string[], contracts: T): Promise<({ address: string } & T)[]> { 7 | const users: ({ address: string } & T)[] = []; 8 | for (const address of addresses) { 9 | users.push(await setupUser(address, contracts)); 10 | } 11 | return users; 12 | } 13 | 14 | export async function setupUser( 15 | address: string, 16 | contracts: T 17 | ): Promise<{ address: string } & T> { 18 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 19 | const user: any = { address }; 20 | for (const key of Object.keys(contracts)) { 21 | user[key] = contracts[key].connect(await ethers.getSigner(address)); 22 | } 23 | return user as { address: string } & T; 24 | } 25 | -------------------------------------------------------------------------------- /apps/hardhat-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/hardhat.json", 3 | "include": [ 4 | "hardhat.config.ts", 5 | "./scripts", 6 | "./deploy", 7 | "./test", 8 | "typechain/**/*" 9 | ], 10 | "exclude": ["node_modules"] 11 | } 12 | -------------------------------------------------------------------------------- /apps/hardhat-ts/utils/network.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import { 3 | HDAccountsUserConfig, 4 | HttpNetworkUserConfig, 5 | NetworksUserConfig, 6 | } from "hardhat/types"; 7 | export function node_url(networkName: string): string { 8 | if (networkName) { 9 | const uri = process.env["ETH_NODE_URI_" + networkName.toUpperCase()]; 10 | if (uri && uri !== "") { 11 | return uri; 12 | } 13 | } 14 | 15 | if (networkName === "localhost") { 16 | // do not use ETH_NODE_URI 17 | return "http://localhost:8545"; 18 | } 19 | 20 | let uri = process.env.ETH_NODE_URI; 21 | if (uri) { 22 | uri = uri.replace("{{networkName}}", networkName); 23 | } 24 | if (!uri || uri === "") { 25 | // throw new Error(`environment variable "ETH_NODE_URI" not configured `); 26 | return ""; 27 | } 28 | if (uri.indexOf("{{") >= 0) { 29 | throw new Error( 30 | `invalid uri or network not supported by node provider : ${uri}` 31 | ); 32 | } 33 | return uri; 34 | } 35 | 36 | export function getMnemonic(networkName?: string): string { 37 | if (networkName) { 38 | const mnemonic = process.env["MNEMONIC_" + networkName.toUpperCase()]; 39 | if (mnemonic && mnemonic !== "") { 40 | return mnemonic; 41 | } 42 | } 43 | 44 | const mnemonic = process.env.MNEMONIC; 45 | if (!mnemonic || mnemonic === "") { 46 | return "test test test test test test test test test test test junk"; 47 | } 48 | return mnemonic; 49 | } 50 | 51 | export function accounts(networkName?: string): { mnemonic: string } { 52 | return { mnemonic: getMnemonic(networkName) }; 53 | } 54 | 55 | export function addForkConfiguration( 56 | networks: NetworksUserConfig 57 | ): NetworksUserConfig { 58 | // While waiting for hardhat PR: https://github.com/nomiclabs/hardhat/pull/1542 59 | if (process.env.HARDHAT_FORK) { 60 | process.env["HARDHAT_DEPLOY_FORK"] = process.env.HARDHAT_FORK; 61 | } 62 | 63 | const currentNetworkName = process.env.HARDHAT_FORK; 64 | let forkURL: string | undefined = 65 | currentNetworkName && node_url(currentNetworkName); 66 | let hardhatAccounts: HDAccountsUserConfig | undefined; 67 | if (currentNetworkName && currentNetworkName !== "hardhat") { 68 | const currentNetwork = networks[ 69 | currentNetworkName 70 | ] as HttpNetworkUserConfig; 71 | if (currentNetwork) { 72 | forkURL = currentNetwork.url; 73 | if ( 74 | currentNetwork.accounts && 75 | typeof currentNetwork.accounts === "object" && 76 | "mnemonic" in currentNetwork.accounts 77 | ) { 78 | hardhatAccounts = currentNetwork.accounts; 79 | } 80 | } 81 | } 82 | 83 | const newNetworks = { 84 | ...networks, 85 | hardhat: { 86 | ...networks.hardhat, 87 | ...{ 88 | accounts: hardhatAccounts, 89 | forking: forkURL 90 | ? { 91 | url: forkURL, 92 | blockNumber: process.env.HARDHAT_FORK_NUMBER 93 | ? parseInt(process.env.HARDHAT_FORK_NUMBER) 94 | : undefined, 95 | } 96 | : undefined, 97 | mining: process.env.MINING_INTERVAL 98 | ? { 99 | auto: false, 100 | interval: process.env.MINING_INTERVAL.split(",").map((v) => 101 | parseInt(v) 102 | ) as [number, number], 103 | } 104 | : undefined, 105 | }, 106 | }, 107 | }; 108 | return newNetworks; 109 | } 110 | -------------------------------------------------------------------------------- /apps/web/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("config/eslint-preset"); 2 | -------------------------------------------------------------------------------- /apps/web/README.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | First, run the development server: 4 | 5 | ```bash 6 | yarn dev 7 | ``` 8 | 9 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 10 | 11 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 12 | 13 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 14 | 15 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 16 | 17 | ## Learn More 18 | 19 | To learn more about Next.js, take a look at the following resources: 20 | 21 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 22 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 23 | 24 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 25 | 26 | ## Deploy on Vercel 27 | 28 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js. 29 | 30 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 31 | -------------------------------------------------------------------------------- /apps/web/ethereum.d.ts: -------------------------------------------------------------------------------- 1 | export declare global { 2 | interface Window { 3 | ethereum: any; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /apps/web/next.config.js: -------------------------------------------------------------------------------- 1 | const withTM = require("next-transpile-modules")(["ui"]); 2 | const path = require("path"); 3 | /** @type {import('next').NextConfig} */ 4 | module.exports = withTM({ 5 | reactStrictMode: true, 6 | webpack: (config, options) => { 7 | if (options.isServer) { 8 | config.externals = [ 9 | "wagmi", 10 | "ethers", 11 | "@headlessui/react", 12 | ...config.externals, 13 | ]; 14 | } 15 | config.resolve.alias["wagmi"] = path.resolve( 16 | __dirname, 17 | ".", 18 | "node_modules", 19 | "wagmi" 20 | ); 21 | config.resolve.alias["ethers"] = path.resolve( 22 | __dirname, 23 | ".", 24 | "node_modules", 25 | "ethers" 26 | ); 27 | config.resolve.alias["@headlessui/react"] = path.resolve( 28 | __dirname, 29 | ".", 30 | "node_modules", 31 | "@headlessui/react" 32 | ); 33 | return config; 34 | }, 35 | }); 36 | -------------------------------------------------------------------------------- /apps/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "lint:fix": "next lint --fix" 11 | }, 12 | "dependencies": { 13 | "@headlessui/react": "^1.4.3", 14 | "ethers": "^5.5.3", 15 | "next": "12.0.7", 16 | "react": "17.0.2", 17 | "react-dom": "17.0.2", 18 | "ui": "workspace:*", 19 | "wagmi": "^0.1.7" 20 | }, 21 | "devDependencies": { 22 | "@ethersproject/abi": "^5.5.0", 23 | "@ethersproject/providers": "^5.5.2", 24 | "@types/react": "17.0.37", 25 | "autoprefixer": "^10.4.0", 26 | "config": "workspace:*", 27 | "eslint": "7.32.0", 28 | "next-transpile-modules": "9.0.0", 29 | "postcss": "^8.4.5", 30 | "tailwindcss": "^3.0.5", 31 | "tsconfig": "workspace:*", 32 | "typescript": "^4.5.3" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("config/postcss.config"); -------------------------------------------------------------------------------- /apps/web/src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "../styles/global.css"; 2 | import { WagmiProvider } from "ui"; 3 | import type { AppProps } from "next/app"; 4 | 5 | function MyApp({ Component, pageProps }: AppProps) { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | } 12 | 13 | export default MyApp; 14 | -------------------------------------------------------------------------------- /apps/web/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button, WalletConnectModal } from "ui"; 2 | import Head from "next/head"; 3 | import { useContractWrite, useAccount } from "wagmi"; 4 | import { Storage__factory, Storage } from "@/typechain"; 5 | import { ethers } from "ethers"; 6 | import * as React from "react"; 7 | 8 | const hasEthereum = 9 | typeof window !== "undefined" && typeof window.ethereum !== "undefined"; 10 | const contractAddress = "0x5FbDB2315678afecb367f032d93F642f64180aa3"; 11 | 12 | export default function Web() { 13 | const inputRef = React.useRef(); 14 | const [status, setStatus] = React.useState<"loading..." | "complete">( 15 | "complete" 16 | ); 17 | const [currentStore, setCurrentStore] = React.useState(""); 18 | const [{ data: account }] = useAccount(); 19 | const [{}, write] = useContractWrite( 20 | { 21 | addressOrName: contractAddress, 22 | contractInterface: Storage__factory.abi, 23 | }, 24 | "set" 25 | ); 26 | 27 | async function handleSubmit(e: React.FormEvent) { 28 | e.preventDefault(); 29 | if (hasEthereum) { 30 | const inputState = inputRef.current.value; 31 | 32 | const tx = await write({ args: inputState }); 33 | setStatus("loading..."); 34 | if (tx.data) { 35 | const receipt = await tx.data.wait(); 36 | if (receipt.status === 1) { 37 | setCurrentStore(inputState); 38 | inputRef.current.value = ""; 39 | } 40 | setStatus("complete"); 41 | } 42 | } 43 | } 44 | 45 | React.useEffect(() => { 46 | async function fetchStore() { 47 | if (hasEthereum) { 48 | const provider = new ethers.providers.Web3Provider( 49 | window.ethereum as any 50 | ); 51 | const storageContract = Storage__factory.connect( 52 | contractAddress, 53 | provider 54 | ); 55 | try { 56 | const data = await storageContract.retrieve(); 57 | setCurrentStore(data); 58 | } catch (err) { 59 | console.log("EfetchStorerror: ", err); 60 | } 61 | } 62 | } 63 | fetchStore(); 64 | }, []); 65 | 66 | return ( 67 |
68 | 69 | Next.js Dapp Starter Ts 70 | 71 | 72 |
73 | <> 74 |

75 | Next.js Dapp Starter Ts 76 |

77 |

Store Value : {currentStore}

78 |

transaction status : {status}

79 |
80 |
81 |
82 | 91 | 98 |
99 |
100 |
101 | 102 |
103 |
104 | 105 |
106 |
107 | ); 108 | } 109 | -------------------------------------------------------------------------------- /apps/web/src/styles/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /apps/web/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("config/tailwind.config"); -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/nextjs.json", 3 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 4 | "exclude": ["node_modules"], 5 | "compilerOptions": { 6 | "baseUrl": "./src", 7 | "paths": { 8 | "@/*": ["*"] 9 | } 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /gitpod-port.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G3root/nextjs-dapp-starter-ts/a3a244eb57e91fbcb6eca8944503d3af987ec83f/gitpod-port.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "turborepo-basic-shared", 3 | "version": "0.0.0", 4 | "private": true, 5 | "repository": "github:G3root/nextjs-dapp-starter-ts", 6 | "author": "G3root", 7 | "license": "MIT", 8 | "keywords": [ 9 | "ethereum", 10 | "smart-contracts", 11 | "template", 12 | "starter", 13 | "hardhat", 14 | "solidity", 15 | "nextjs", 16 | "typescript" 17 | ], 18 | "scripts": { 19 | "build": "turbo run build", 20 | "dev": "turbo run dev --parallel", 21 | "lint": "turbo run lint", 22 | "lint:fix": "turbo run lint:fix", 23 | "format": "prettier --write \"**/*.{ts,tsx,md}\"" 24 | }, 25 | "devDependencies": { 26 | "prettier": "^2.5.1", 27 | "turbo": "latest", 28 | "ts-node": "^10.4.0" 29 | }, 30 | "turbo": { 31 | "pipeline": { 32 | "build": { 33 | "dependsOn": [ 34 | "^build" 35 | ], 36 | "outputs": [ 37 | ".next/**", 38 | "artifacts/**", 39 | "cache/**", 40 | "typechain/**" 41 | ] 42 | }, 43 | "lint": { 44 | "outputs": [] 45 | }, 46 | "lint:fix": { 47 | "outputs": [] 48 | }, 49 | "dev": { 50 | "cache": false 51 | } 52 | } 53 | }, 54 | "engines": { 55 | "npm": ">=7.0.0", 56 | "node": ">=14.0.0" 57 | } 58 | } -------------------------------------------------------------------------------- /packages/config/eslint-preset.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["next", "prettier"], 3 | settings: { 4 | next: { 5 | rootDir: ["apps/*/", "packages/*/"], 6 | }, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /packages/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "config", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "files": [ 7 | "eslint-preset.js", 8 | "tailwind.config.js", 9 | "postcss.config.js" 10 | ], 11 | "dependencies": { 12 | "eslint-config-next": "^12.0.3", 13 | "eslint-config-prettier": "^8.3.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/config/postcss.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | plugins: { 4 | tailwindcss: {}, 5 | autoprefixer: {}, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /packages/config/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['../../packages/ui/**/*.{js,ts,jsx,tsx}','./src/**/*.{js,ts,jsx,tsx}'], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | } 8 | -------------------------------------------------------------------------------- /packages/tsconfig/README.md: -------------------------------------------------------------------------------- 1 | # `tsconfig` 2 | 3 | These are base shared `tsconfig.json`s from which all other `tsconfig.json`'s inherit from. 4 | -------------------------------------------------------------------------------- /packages/tsconfig/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "composite": false, 6 | "declaration": true, 7 | "declarationMap": true, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "inlineSources": false, 11 | "isolatedModules": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "preserveWatchOutput": true, 15 | "skipLibCheck": true, 16 | "strict": true 17 | }, 18 | "exclude": ["node_modules"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/tsconfig/hardhat.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "hardhat", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "target": "es5", 7 | "module": "commonjs", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "moduleResolution": "node", 11 | "forceConsistentCasingInFileNames": true, 12 | "outDir": "dist" 13 | }, 14 | "include": [ 15 | "hardhat.config.ts", 16 | "./scripts", 17 | "./deploy", 18 | "./test", 19 | "typechain/**/*" 20 | ], 21 | "exclude": ["node_modules"] 22 | } 23 | -------------------------------------------------------------------------------- /packages/tsconfig/nextjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Next.js", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "target": "es5", 7 | "lib": ["dom", "dom.iterable", "esnext"], 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "strict": false, 11 | "forceConsistentCasingInFileNames": true, 12 | "noEmit": true, 13 | "incremental": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve" 20 | }, 21 | "include": ["src", "next-env.d.ts"], 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /packages/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tsconfig", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "index.js", 6 | "files": [ 7 | "base.json", 8 | "nextjs.json", 9 | "react-library.json", 10 | "hardhat.json" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/tsconfig/react-library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React Library", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "lib": ["ES2015"], 7 | "module": "ESNext", 8 | "target": "ES6", 9 | "jsx": "react-jsx", 10 | "moduleResolution": "node" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/ui/Button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import clsx from "clsx"; 3 | export interface IButtonProps 4 | extends React.DetailedHTMLProps< 5 | React.ButtonHTMLAttributes, 6 | HTMLButtonElement 7 | > {} 8 | 9 | export function Button({ children, className, ...rest }: IButtonProps) { 10 | return ( 11 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /packages/ui/WagmiProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Connector, Provider, chain, defaultChains } from "wagmi"; 3 | import { InjectedConnector } from "wagmi/connectors/injected"; 4 | import { WalletConnectConnector } from "wagmi/connectors/walletConnect"; 5 | import { providers } from "ethers"; 6 | // Get environment variables 7 | 8 | // Pick chains 9 | const chains = defaultChains; 10 | 11 | const connectors = [ 12 | new InjectedConnector({ chains }), 13 | new WalletConnectConnector({ 14 | options: { 15 | qrcode: true, 16 | }, 17 | }), 18 | ]; 19 | 20 | export interface IWagmiProviderProps { 21 | children: React.ReactNode; 22 | } 23 | 24 | export function WagmiProvider({ children }: IWagmiProviderProps) { 25 | return ( 26 | 27 | {children} 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /packages/ui/WalletConnectModal.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Dialog, Transition } from "@headlessui/react"; 3 | import { Button } from "./Button"; 4 | import { useConnect, useAccount } from "wagmi"; 5 | export interface IWalletConnectModalProps {} 6 | const useIsMounted = () => { 7 | const [mounted, setMounted] = React.useState(false); 8 | 9 | React.useEffect(() => setMounted(true), []); 10 | 11 | return mounted; 12 | }; 13 | export function WalletConnectModal(props: IWalletConnectModalProps) { 14 | const [isOpen, setIsOpen] = React.useState(false); 15 | const isMounted = useIsMounted(); 16 | const [ 17 | { 18 | data: { connector, connectors }, 19 | error, 20 | loading, 21 | }, 22 | connect, 23 | ] = useConnect(); 24 | const [ 25 | { data: account, error: accountError, loading: accountLoading }, 26 | disconnect, 27 | ] = useAccount(); 28 | function handleModal() { 29 | setIsOpen((state) => !state); 30 | } 31 | function handleConnect() { 32 | if (!accountLoading && account?.address) { 33 | disconnect(); 34 | } else { 35 | handleModal(); 36 | } 37 | } 38 | return ( 39 | <> 40 | 41 | 46 |
47 | 56 | 57 | 58 | 59 | {/* This element is to trick the browser into centering the modal contents. */} 60 | 66 | 75 |
76 |
77 | 81 | connect Wallet 82 | 83 | 105 |
106 |
107 | {connectors.map((x) => ( 108 | 125 | ))} 126 |
127 |
128 |
129 |
130 |
131 |
132 | 137 | 138 | ); 139 | } 140 | -------------------------------------------------------------------------------- /packages/ui/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Button"; 2 | export * from "./WalletConnectModal"; 3 | export * from "./WagmiProvider"; 4 | -------------------------------------------------------------------------------- /packages/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui", 3 | "version": "0.0.0", 4 | "main": "index.ts", 5 | "license": "MIT", 6 | "type": "module", 7 | "devDependencies": { 8 | "@headlessui/react": "^1.4.3", 9 | "@types/react": "^17.0.37", 10 | "@types/react-dom": "^17.0.11", 11 | "clsx": "^1.1.1", 12 | "ethers": "^5.5.3", 13 | "tsconfig": "workspace:*", 14 | "typescript": "^4.5.3", 15 | "wagmi": "^0.1.7" 16 | }, 17 | "peerDependencies": { 18 | "@headlessui/react": "^1.4.3", 19 | "ethers": "^5.5.3", 20 | "wagmi": "^0.1.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "apps/*" 3 | - "packages/*" 4 | -------------------------------------------------------------------------------- /wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G3root/nextjs-dapp-starter-ts/a3a244eb57e91fbcb6eca8944503d3af987ec83f/wallet.png --------------------------------------------------------------------------------