Initial commit: add .gitignore and README
This commit is contained in:
74
dapps/quorumToken/README.md
Normal file
74
dapps/quorumToken/README.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Using a DApp to interact with the blockchain
|
||||
|
||||
This DApp, uses Hardhat and Ethers.js in combination with a self custodial (also called a user controlled) wallet i.e. Metamask to interact with the chain. As such this process esentially comprises two parts:
|
||||
|
||||
1. Deploy the contract to the chain
|
||||
2. Use the DApp's interface to send and transact on the chain
|
||||
|
||||
The `dapps/quorumToken` folder is this structured in this manner (only relevant paths shown):
|
||||
|
||||
```
|
||||
quorumToken
|
||||
├── hardhat.config.ts // hardhat network config
|
||||
├── contracts // sample contracts of which we use the QuorumToken.sol
|
||||
├── scripts // handy scripts eg: to deploy to a chain
|
||||
├── test // contract tests
|
||||
└── frontend // DApp done in next.js
|
||||
├── README.md
|
||||
├── public
|
||||
├── src
|
||||
├── styles
|
||||
├── tsconfig.json
|
||||
```
|
||||
|
||||
# Contracts
|
||||
|
||||
Contracts are written in Solidity and we use the hardhat development environment for testing, deploying etc
|
||||
|
||||
The `hardhat.config.js` specifies the networks, accounts, solidity version etc
|
||||
|
||||
Install dependencies
|
||||
|
||||
```
|
||||
npm i
|
||||
```
|
||||
|
||||
Compile the contracts and run tests (optional):
|
||||
|
||||
```
|
||||
npx run compile
|
||||
# As you develop contracts you are using the inbuilt `hardhat` network
|
||||
npx hardhat test
|
||||
```
|
||||
|
||||
Deploy contracts with:
|
||||
|
||||
```
|
||||
# we specify the network here so the DApp can use the contract, but you can use any network you wish to and remember to connect Metamask to the appropriate network for the DApp
|
||||
npx hardhat run ./scripts/deploy_quorumtoken.ts --network quickstart
|
||||
```
|
||||
|
||||
_Please remember to save the address returned from the deploy as you will need it for the following steps_
|
||||
|
||||
# DApp
|
||||
|
||||
We have a sample DApp created that uses Next.js, react and ethers to interact with the quickstart network
|
||||
|
||||
```
|
||||
cd frontend
|
||||
npm i
|
||||
npm run dev
|
||||
```
|
||||
|
||||
1. Open up a tab on port 3001 and connect to Metamask.
|
||||
2. To interact with the DApp you will need to import the test accounts from `hardhat.config.ts`
|
||||
|
||||
For brevity they are the following:
|
||||
|
||||
```
|
||||
0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63
|
||||
0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3
|
||||
0xae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f
|
||||
```
|
||||
|
||||
3. When you connect to Metamask, you are presented with a field to input the address of the deployed contract from the previous step. The app will then fetch the contract data and you can then transfer eth to a new another account.
|
||||
12
dapps/quorumToken/contracts/QuorumToken.sol
Normal file
12
dapps/quorumToken/contracts/QuorumToken.sol
Normal file
@@ -0,0 +1,12 @@
|
||||
//SPDX-License-Identifier: Unlicense
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
contract QuorumToken is ERC20 {
|
||||
constructor(uint256 initialSupply)
|
||||
ERC20("QuorumToken", "QT")
|
||||
{
|
||||
_mint(msg.sender, initialSupply);
|
||||
}
|
||||
}
|
||||
3
dapps/quorumToken/frontend/.eslintrc.json
Normal file
3
dapps/quorumToken/frontend/.eslintrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
6
dapps/quorumToken/frontend/next.config.js
Normal file
6
dapps/quorumToken/frontend/next.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
5612
dapps/quorumToken/frontend/package-lock.json
generated
Normal file
5612
dapps/quorumToken/frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
dapps/quorumToken/frontend/package.json
Normal file
29
dapps/quorumToken/frontend/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "webapp",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev -p 3001",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/react": "^2.8.0",
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"ethers": "6.7.1",
|
||||
"framer-motion": "^10.16.1",
|
||||
"next": "13.4.19",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "20.5.4",
|
||||
"@types/react": "18.2.21",
|
||||
"@types/react-dom": "18.2.7",
|
||||
"eslint": "8.47.0",
|
||||
"eslint-config-next": "13.4.19",
|
||||
"typescript": "5.1.6"
|
||||
}
|
||||
}
|
||||
14
dapps/quorumToken/frontend/public/favicon.svg
Normal file
14
dapps/quorumToken/frontend/public/favicon.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="313" height="311" viewBox="0 0 313 311" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
path {
|
||||
fill: black;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path {
|
||||
fill: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<path d="M 297 155.5 C 297 233.1 234.1 296 156.5 296 C 78.9 296 16 233.1 16 155.5 C 16 77.9 78.9 15 156.5 15 C 234.1 15 297 77.9 297 155.5 Z M 235.952 170.863 C 228.56 170.91 222.596 176.865 222.632 184.184 C 222.632 184.196 222.632 184.196 222.632 184.208 C 222.596 191.515 228.56 197.47 235.952 197.505 C 243.345 197.458 249.297 191.504 249.273 184.208 C 249.333 176.9 243.381 170.922 235.988 170.863 C 235.976 170.863 235.964 170.863 235.952 170.863 Z M 232.204 155.603 C 238.57 151.928 240.766 143.779 237.09 137.415 C 233.414 131.051 225.263 128.855 218.897 132.531 C 212.554 136.182 210.358 144.272 213.964 150.636 L 214.046 150.754 C 217.722 157.106 225.838 159.278 232.192 155.603 C 232.192 155.615 232.204 155.615 232.204 155.603 Z M 94.567 132.603 C 88.198 128.915 80.042 131.076 76.352 137.442 C 72.662 143.808 74.824 151.959 81.194 155.646 C 87.563 159.334 95.719 157.173 99.409 150.808 L 99.409 150.761 C 103.076 144.407 100.913 136.279 94.567 132.603 Z M 116.395 128.926 C 123.761 128.914 129.726 122.935 129.714 115.581 C 129.714 113.279 129.114 111.023 127.973 109.026 C 124.361 102.648 116.242 100.392 109.842 104.01 C 109.83 104.01 109.83 104.022 109.818 104.022 L 109.595 104.151 C 103.265 107.91 101.182 116.086 104.947 122.406 C 107.348 126.435 111.689 128.914 116.395 128.926 Z M 156.385 91.42 C 149.032 91.42 143.064 97.387 143.064 104.741 C 143.064 112.094 149.032 118.062 156.385 118.062 C 163.739 118.062 169.706 112.094 169.706 104.741 C 169.706 97.387 163.739 91.42 156.385 91.42 Z M 113.889 170.873 C 106.535 170.873 100.568 176.84 100.568 184.194 C 100.568 191.547 106.535 197.515 113.889 197.515 C 121.242 197.515 127.21 191.547 127.21 184.194 C 127.21 176.84 121.242 170.873 113.889 170.873 Z M 178.656 134.517 C 171.303 134.517 165.335 140.484 165.335 147.838 C 165.335 155.191 171.303 161.159 178.656 161.159 C 186.01 161.159 191.977 155.191 191.977 147.838 C 191.977 140.484 186.01 134.517 178.656 134.517 Z M 189.788 127.625 L 189.96 127.721 C 196.823 131.132 205.197 128.432 208.672 121.696 C 211.876 115.479 209.801 107.887 203.846 104.079 C 197.007 100.621 188.609 103.248 185.085 109.96 C 181.819 116.165 183.845 123.781 189.788 127.625 Z M 77.307 170.863 C 69.957 170.745 63.88 176.59 63.727 183.942 C 63.727 184.024 63.727 184.13 63.727 184.213 C 63.75 191.577 69.722 197.527 77.083 197.504 C 84.444 197.48 90.392 191.506 90.369 184.142 C 90.345 176.908 84.538 171.005 77.307 170.863 Z M 198.428 170.833 C 191.074 170.833 185.107 176.801 185.107 184.154 C 185.107 191.508 191.074 197.475 198.428 197.475 C 205.781 197.475 211.749 191.508 211.749 184.154 C 211.749 176.801 205.781 170.833 198.428 170.833 Z M 142.078 158.915 C 148.201 154.855 149.866 146.594 145.808 140.469 C 142.113 134.895 134.852 132.935 128.858 135.88 L 128.776 135.927 C 122.196 139.19 119.51 147.181 122.771 153.764 C 126.031 160.347 134.019 163.034 140.6 159.772 C 141.116 159.525 141.608 159.232 142.078 158.915 Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
1
dapps/quorumToken/frontend/public/next.svg
Normal file
1
dapps/quorumToken/frontend/public/next.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
27
dapps/quorumToken/frontend/src/components/Layout.tsx
Normal file
27
dapps/quorumToken/frontend/src/components/Layout.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React, { ReactNode } from 'react'
|
||||
import { Container, Flex, useColorModeValue, Spacer, Heading, Center, Text } from '@chakra-ui/react'
|
||||
|
||||
interface LayoutProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function Layout({ children }: LayoutProps) {
|
||||
return (
|
||||
<div>
|
||||
|
||||
<Flex w="100%" bg={useColorModeValue('gray.100', 'gray.900')} px="6" py="5" align="center" justify="space-between">
|
||||
<Heading size="md">Quorum Quickstart DApp</Heading>
|
||||
<Spacer />
|
||||
</Flex>
|
||||
|
||||
<Container maxW="container.lg" py='8'>
|
||||
{children}
|
||||
</Container>
|
||||
|
||||
<Center as="footer" bg={useColorModeValue('gray.100', 'gray.700')} p={6}>
|
||||
<Text fontSize="md"> © {new Date().getFullYear()} ConsenSys Software, Inc. All rights reserved.</Text>
|
||||
</Center>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
25
dapps/quorumToken/frontend/src/components/MMAccount.tsx
Normal file
25
dapps/quorumToken/frontend/src/components/MMAccount.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
import React, {useEffect, useState } from 'react';
|
||||
import { Heading, Text, VStack, Box, Button, Input, Spacer, Flex } from '@chakra-ui/react'
|
||||
|
||||
interface MMAccountProps {
|
||||
balance: string | undefined,
|
||||
chainId: number | undefined
|
||||
erc20ContractAddress: string
|
||||
deployedAddressHandler: any
|
||||
}
|
||||
|
||||
export default function MMAccount(props:MMAccountProps){
|
||||
|
||||
return (
|
||||
<Box mb={0} p={4} w='100%' borderWidth="1px" borderRadius="lg">
|
||||
<Heading my={4} fontSize='xl'>Account</Heading>
|
||||
<Text my={4}>Details of the account connected to Metamask</Text>
|
||||
<Text><b>Balance of current account (ETH)</b>: {props.balance}</Text>
|
||||
<Text><b>ChainId</b>: {props.chainId} </Text>
|
||||
{/* todo: fix formatting here */}
|
||||
<Text><b>Address that the QuorumToken was deployed to</b>: </Text>
|
||||
<Input value={props.erc20ContractAddress} name="erc20ContractAddress" onChange={props.deployedAddressHandler} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// In order to interact with a contract from js, we need the contract's ABI
|
||||
// Im using a human readable format here, but you can just as easily use the compiled output from HardHat
|
||||
|
||||
export const QuorumTokenABI = [
|
||||
// Read-Only Functions
|
||||
"function balanceOf(address owner) view returns (uint256)",
|
||||
"function totalSupply() view returns (uint256)",
|
||||
"function decimals() view returns (uint8)",
|
||||
"function symbol() view returns (string)",
|
||||
// Authenticated Functions
|
||||
"function transfer(address to, uint amount) returns (bool)",
|
||||
// Events
|
||||
"event Transfer(address indexed from, address indexed to, uint amount)"
|
||||
];
|
||||
@@ -0,0 +1,88 @@
|
||||
import React, {useEffect, useState } from 'react';
|
||||
import {Text} from '@chakra-ui/react'
|
||||
import {QuorumTokenABI as abi} from './QuorumTokenABI'
|
||||
import {ethers, Contract} from 'ethers'
|
||||
|
||||
declare let window: any;
|
||||
|
||||
interface ReadQuorumTokenProps {
|
||||
addressContract: string,
|
||||
currentAccount: string | undefined
|
||||
}
|
||||
|
||||
export default function ReadQuorumToken(props:ReadQuorumTokenProps){
|
||||
const addressContract = props.addressContract
|
||||
const currentAccount = props.currentAccount
|
||||
const [totalSupply,setTotalSupply]=useState<string>()
|
||||
const [symbol,setSymbol]= useState<string>("")
|
||||
const [balance, setBalance] =useState<number|undefined>(undefined)
|
||||
|
||||
useEffect( () => {
|
||||
if(!window.ethereum) return;
|
||||
const provider = new ethers.BrowserProvider(window.ethereum);
|
||||
const erc20:Contract = new ethers.Contract(addressContract, abi, provider);
|
||||
|
||||
provider.getCode(addressContract).then((result:string)=>{
|
||||
//check whether it is a contract
|
||||
if(result === '0x') return
|
||||
|
||||
erc20.symbol().then((result:string)=>{
|
||||
setSymbol(result)
|
||||
}).catch('error', console.error)
|
||||
erc20.totalSupply().then((result:string)=>{
|
||||
setTotalSupply(ethers.formatEther(result))
|
||||
}).catch('error', console.error);
|
||||
|
||||
})
|
||||
},[])
|
||||
|
||||
// when currentAccount changes, we call this hook ie useEffect(()=>{ .... },[currentAccount]
|
||||
//
|
||||
useEffect(()=>{
|
||||
if(!window.ethereum) return
|
||||
if(!currentAccount) return
|
||||
|
||||
queryTokenBalance(window);
|
||||
const provider = new ethers.BrowserProvider(window.ethereum);
|
||||
const erc20:Contract = new ethers.Contract(addressContract, abi, provider);
|
||||
|
||||
// listen for changes on an Ethereum address
|
||||
console.log(`listening for Transfer...`)
|
||||
const fromMe = erc20.filters.Transfer(currentAccount, null)
|
||||
erc20.on(fromMe, (from, to, amount, event) => {
|
||||
console.log('Transfer|sent', {from, to, amount, event} )
|
||||
queryTokenBalance(window)
|
||||
})
|
||||
|
||||
const toMe = erc20.filters.Transfer(null, currentAccount)
|
||||
erc20.on(toMe, (from, to, amount, event) => {
|
||||
console.log('Transfer|received', {from, to, amount, event} )
|
||||
queryTokenBalance(window)
|
||||
})
|
||||
|
||||
// remove listener when the component is unmounted
|
||||
return () => {
|
||||
erc20.removeAllListeners(toMe)
|
||||
erc20.removeAllListeners(fromMe)
|
||||
}
|
||||
}, [currentAccount])
|
||||
|
||||
|
||||
async function queryTokenBalance(window:any){
|
||||
const provider = new ethers.BrowserProvider(window.ethereum);
|
||||
const erc20:Contract = new ethers.Contract(addressContract, abi, provider);
|
||||
|
||||
erc20.balanceOf(currentAccount)
|
||||
.then((result:string)=>{
|
||||
setBalance(Number(ethers.formatEther(result)))
|
||||
}).catch((e:Error)=>console.log(e))
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Text><b>ERC20 Contract Address</b>: {addressContract}</Text>
|
||||
<Text><b>QuorumToken totalSupply</b>: {totalSupply} {symbol}</Text>
|
||||
<Text><b>QuorumToken in current account</b>: {balance} {symbol}</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Text, Button, Input , NumberInput, NumberInputField, FormControl, FormLabel } from '@chakra-ui/react';
|
||||
import {ethers, Contract} from 'ethers';
|
||||
import {QuorumTokenABI as abi} from './QuorumTokenABI';
|
||||
import { TransactionResponse,TransactionReceipt } from "@ethersproject/abstract-provider";
|
||||
|
||||
declare let window: any;
|
||||
|
||||
interface Props {
|
||||
addressContract: string,
|
||||
currentAccount: string | undefined
|
||||
}
|
||||
|
||||
export default function TransferQuorumToken(props:Props){
|
||||
const addressContract = props.addressContract
|
||||
const currentAccount = props.currentAccount
|
||||
const [amount, setAmount]=useState<string>('100')
|
||||
const [toAddress, setToAddress]=useState<string>("")
|
||||
|
||||
const handleChange = (value:string) => setAmount(value)
|
||||
|
||||
// https://docs.ethers.org/v6/getting-started/#starting-contracts
|
||||
async function transfer(event:React.FormEvent) {
|
||||
event.preventDefault()
|
||||
// const provider = new ethers.JsonRpcProvider('http://127.0.0.1:8545');
|
||||
const provider = new ethers.BrowserProvider(window.ethereum);
|
||||
const signer = await provider.getSigner();
|
||||
const erc20:Contract = new ethers.Contract(addressContract, abi, signer);
|
||||
|
||||
erc20.transfer(toAddress, ethers.parseEther(amount))
|
||||
.then((tr: TransactionResponse) => {
|
||||
console.log(`TransactionResponse TX hash: ${tr.hash}`)
|
||||
// todo: maybe put this in a modal thing?
|
||||
tr.wait().then((receipt:TransactionReceipt)=>{console.log("transfer receipt",receipt)})
|
||||
})
|
||||
.catch((e:Error)=>console.log(e))
|
||||
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<form onSubmit={transfer}>
|
||||
<FormControl>
|
||||
<FormLabel htmlFor='amount'>Amount: </FormLabel>
|
||||
<NumberInput defaultValue={amount} min={10} max={1000} onChange={handleChange}>
|
||||
<NumberInputField />
|
||||
</NumberInput>
|
||||
<FormLabel htmlFor='toaddress'>To address: </FormLabel>
|
||||
<Input id="toaddress" type="text" required onChange={(e) => setToAddress(e.target.value)} my={3}/>
|
||||
<Button type="submit" isDisabled={!currentAccount}>Transfer</Button>
|
||||
</FormControl>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
19
dapps/quorumToken/frontend/src/pages/_app.tsx
Normal file
19
dapps/quorumToken/frontend/src/pages/_app.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
import type { AppProps } from "next/app";
|
||||
import { ChakraProvider } from "@chakra-ui/react";
|
||||
import "../../styles/globals.css";
|
||||
import Layout from '../components/Layout';
|
||||
|
||||
function MyApp({ Component, pageProps, router }: AppProps) {
|
||||
|
||||
return (
|
||||
<ChakraProvider>
|
||||
<title>Quorum Quickstart DApp</title>
|
||||
<Layout>
|
||||
<Component {...pageProps} />
|
||||
</Layout>
|
||||
</ChakraProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default MyApp;
|
||||
19
dapps/quorumToken/frontend/src/pages/_document.js
Normal file
19
dapps/quorumToken/frontend/src/pages/_document.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ColorModeScript } from "@chakra-ui/react";
|
||||
import NextDocument, { Html, Head, Main, NextScript } from "next/document";
|
||||
|
||||
export default class Document extends NextDocument {
|
||||
render() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
<link rel="shortcut icon" href="/explorer/images/favicon.svg" />
|
||||
</Head>
|
||||
<body>
|
||||
<ColorModeScript />
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
106
dapps/quorumToken/frontend/src/pages/index.tsx
Normal file
106
dapps/quorumToken/frontend/src/pages/index.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import type { NextPage } from 'next'
|
||||
import { Heading, Text, VStack, Box, Button, Input, Spacer, Flex } from '@chakra-ui/react'
|
||||
import { useState, useEffect} from 'react'
|
||||
import {ethers} from "ethers"
|
||||
import ReadQuorumToken from "../components/quorumToken/ReadQuorumToken"
|
||||
import TransferQuorumToken from "../components/quorumToken/TransferQuorumToken"
|
||||
import MMAccount from "../components/MMAccount"
|
||||
|
||||
declare let window:any
|
||||
|
||||
export default function Home() {
|
||||
|
||||
const [balance, setBalance] = useState<string | undefined>();
|
||||
const [currentAccount, setCurrentAccount] = useState<string | undefined>();
|
||||
const [erc20ContractAddress, setErc20ContractAddress] = useState<string>("0x");
|
||||
const [chainId, setChainId] = useState<number | undefined>();
|
||||
|
||||
useEffect( () => {
|
||||
if(!currentAccount || !ethers.isAddress(currentAccount)) return;
|
||||
if(!window.ethereum) return;
|
||||
const provider = new ethers.BrowserProvider(window.ethereum);
|
||||
provider.getBalance(currentAccount).then((result)=> {
|
||||
setBalance(ethers.formatEther(result));
|
||||
})
|
||||
provider.getNetwork().then((result)=>{
|
||||
setChainId(ethers.toNumber(result.chainId));
|
||||
})
|
||||
|
||||
},[currentAccount])
|
||||
|
||||
const onClickConnect = () => {
|
||||
if(!window.ethereum) {
|
||||
console.log("please install MetaMask");
|
||||
return;
|
||||
}
|
||||
|
||||
const provider = new ethers.BrowserProvider(window.ethereum);
|
||||
// MetaMask requires requesting permission to connect users accounts
|
||||
provider.send("eth_requestAccounts", [])
|
||||
.then((accounts)=>{
|
||||
if(accounts.length>0) setCurrentAccount(accounts[0])
|
||||
})
|
||||
.catch((e)=>console.log(e))
|
||||
}
|
||||
|
||||
const onClickDisconnect = () => {
|
||||
setBalance(undefined)
|
||||
setCurrentAccount(undefined)
|
||||
}
|
||||
|
||||
const deployedAddressHandler = (e: any) => {
|
||||
setErc20ContractAddress(e.target.value);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading as="h3" my={4}>QuorumToken</Heading>
|
||||
<VStack>
|
||||
<Box w='100%' my={4}>
|
||||
{currentAccount
|
||||
? <Button type="button" w='100%' onClick={onClickDisconnect}>
|
||||
Connected to Metamask with account: {currentAccount}
|
||||
</Button>
|
||||
: <Button type="button" w='100%' onClick={onClickConnect}>
|
||||
Connect to MetaMask
|
||||
</Button>
|
||||
}
|
||||
</Box>
|
||||
{currentAccount
|
||||
?<MMAccount
|
||||
balance={balance}
|
||||
chainId={chainId}
|
||||
erc20ContractAddress={erc20ContractAddress}
|
||||
deployedAddressHandler={deployedAddressHandler} />
|
||||
:<></>
|
||||
}
|
||||
|
||||
{(erc20ContractAddress!="0x")
|
||||
?<Box mb={0} p={4} w='100%' borderWidth="1px" borderRadius="lg">
|
||||
<Heading my={4} fontSize='xl'>Read QuorumToken</Heading>
|
||||
<Text my={4}>Query the smart contract info at address provided</Text>
|
||||
<Spacer />
|
||||
<ReadQuorumToken
|
||||
addressContract={erc20ContractAddress}
|
||||
currentAccount={currentAccount}
|
||||
/>
|
||||
</Box>
|
||||
:<></>
|
||||
}
|
||||
|
||||
{(erc20ContractAddress!="0x")
|
||||
?<Box mb={0} p={4} w='100%' borderWidth="1px" borderRadius="lg">
|
||||
<Heading my={4} fontSize='xl'>Transfer QuorumToken</Heading>
|
||||
<Text my={4}>Interact with the token</Text>
|
||||
<TransferQuorumToken
|
||||
addressContract={erc20ContractAddress}
|
||||
currentAccount={currentAccount}
|
||||
/>
|
||||
</Box>
|
||||
:<></>
|
||||
}
|
||||
|
||||
</VStack>
|
||||
</>
|
||||
)
|
||||
}
|
||||
21
dapps/quorumToken/frontend/styles/globals.css
Normal file
21
dapps/quorumToken/frontend/styles/globals.css
Normal file
@@ -0,0 +1,21 @@
|
||||
:root,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#__next {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
}
|
||||
20
dapps/quorumToken/frontend/tsconfig.json
Normal file
20
dapps/quorumToken/frontend/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"baseUrl": "./src"
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
41
dapps/quorumToken/hardhat.config.ts
Normal file
41
dapps/quorumToken/hardhat.config.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
// https://hardhat.org/hardhat-runner/docs/config
|
||||
import { HardhatUserConfig } from "hardhat/config";
|
||||
import "@nomicfoundation/hardhat-toolbox";
|
||||
|
||||
module.exports = {
|
||||
networks: {
|
||||
// in built test network to use when developing contracts
|
||||
hardhat: {
|
||||
chainId: 1337
|
||||
},
|
||||
quickstart: {
|
||||
url: "http://127.0.0.1:8545",
|
||||
chainId: 1337,
|
||||
// test accounts only, all good ;)
|
||||
accounts: [
|
||||
"0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
|
||||
"0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
|
||||
"0xae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f"
|
||||
]
|
||||
}
|
||||
},
|
||||
defaultNetwork: "hardhat",
|
||||
solidity: {
|
||||
version: "0.8.19",
|
||||
settings: {
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 200
|
||||
}
|
||||
}
|
||||
},
|
||||
paths: {
|
||||
sources: "./contracts",
|
||||
tests: "./test",
|
||||
cache: "./cache",
|
||||
artifacts: "./artifacts"
|
||||
},
|
||||
mocha: {
|
||||
timeout: 40000
|
||||
}
|
||||
}
|
||||
8901
dapps/quorumToken/package-lock.json
generated
Normal file
8901
dapps/quorumToken/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
34
dapps/quorumToken/package.json
Normal file
34
dapps/quorumToken/package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "quorumToken",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"compile": "npx hardhat compile",
|
||||
"test": "npx hardhat test",
|
||||
"deploy-quorumtoken": "npx hardhat run ./scripts/deploy_quorumtoken.ts --network quickstart "
|
||||
},
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "^4.9.3",
|
||||
"hardhat": "^2.17.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^2.0.0",
|
||||
"@nomicfoundation/hardhat-ethers": "^3.0.0",
|
||||
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
|
||||
"@nomicfoundation/hardhat-toolbox": "^3.0.0",
|
||||
"@nomicfoundation/hardhat-verify": "^1.0.0",
|
||||
"@typechain/ethers-v6": "^0.4.0",
|
||||
"@typechain/hardhat": "^8.0.0",
|
||||
"@types/chai": "^4.2.0",
|
||||
"@types/mocha": ">=9.1.0",
|
||||
"@types/node": ">=16.0.0",
|
||||
"chai": "^4.2.0",
|
||||
"ethers": "6.7.1",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
"solidity-coverage": "^0.8.0",
|
||||
"ts-node": ">=8.0.0",
|
||||
"typechain": "^8.1.0",
|
||||
"typescript": ">=4.5.0"
|
||||
}
|
||||
}
|
||||
15
dapps/quorumToken/scripts/deploy_quorumtoken.ts
Normal file
15
dapps/quorumToken/scripts/deploy_quorumtoken.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ethers } from "hardhat"
|
||||
|
||||
async function main() {
|
||||
const initialSupply = ethers.parseEther('10000.0')
|
||||
const QuorumToken = await ethers.getContractFactory("QuorumToken")
|
||||
const deploy = await QuorumToken.deploy(initialSupply)
|
||||
console.log("Contract deploy at: %s", await deploy.getAddress());
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main().catch((error) => {
|
||||
console.error(error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
42
dapps/quorumToken/test/QuorumToken.test.ts
Normal file
42
dapps/quorumToken/test/QuorumToken.test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import {
|
||||
time,
|
||||
loadFixture,
|
||||
} from "@nomicfoundation/hardhat-toolbox/network-helpers";
|
||||
import { expect } from "chai"
|
||||
import { ethers } from "hardhat"
|
||||
import { Signer } from "ethers"
|
||||
|
||||
describe("QuorumToken", function () {
|
||||
const initialSupply = ethers.parseEther('10000.0')
|
||||
|
||||
// We define a fixture to reuse the same setup in every test.
|
||||
// ie a fixture is a function that is only ran the first time it is invoked (& a snapshot is made of the hardhat network).
|
||||
// On all subsequent invocations our fixture won’t be invoked, but rather the snapshot state is reset and loaded
|
||||
async function deployQuorumTokenFixture() {
|
||||
// Contracts are deployed using the first signer/account by default
|
||||
const [owner, otherAccount] = await ethers.getSigners();
|
||||
const QuorumToken = await ethers.getContractFactory("QuorumToken")
|
||||
const quorumToken = await QuorumToken.deploy(initialSupply);
|
||||
const address = await quorumToken.getAddress();
|
||||
return { quorumToken, address, owner, otherAccount };
|
||||
}
|
||||
|
||||
describe("Deployment", function () {
|
||||
it("Should have the correct initial supply", async function () {
|
||||
const {quorumToken, address} = await loadFixture(deployQuorumTokenFixture);
|
||||
expect(await quorumToken.totalSupply()).to.equal(initialSupply);
|
||||
});
|
||||
|
||||
it("Should token transfer with correct balance", async function () {
|
||||
const {quorumToken, address, owner, otherAccount} = await loadFixture(deployQuorumTokenFixture);
|
||||
const amount = ethers.parseEther('200.0')
|
||||
const accountAddress = await otherAccount.getAddress();
|
||||
await expect(async () => quorumToken.transfer(accountAddress,amount))
|
||||
.to.changeTokenBalance(quorumToken, otherAccount, amount)
|
||||
await expect(async () => quorumToken.connect(otherAccount).transfer(await owner.getAddress(),amount))
|
||||
.to.changeTokenBalance(quorumToken, owner, amount)
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})
|
||||
11
dapps/quorumToken/tsconfig.json
Normal file
11
dapps/quorumToken/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user