diff --git a/copy_evm_deployments_to_frontend.sh b/copy_evm_deployments_to_frontend.sh index 921c915..eb37cf1 100755 --- a/copy_evm_deployments_to_frontend.sh +++ b/copy_evm_deployments_to_frontend.sh @@ -1,5 +1,6 @@ -rm "./frontend/src/evm-output/*" +rm ./frontend/src/evm-output/* cp "./evm/ignition/deployments/chain-31337/deployed_addresses.json" "./frontend/src/evm-output/deployed_addresses.json" cp "./evm/ignition/deployments/chain-31337/artifacts/MessageBoxModule#MessageBox.json" "./frontend/src/evm-output/MessageBox.artifacts.json" +cp "./evm/ignition/deployments/chain-31337/artifacts/MainModule#PADToken.json" "./frontend/src/evm-output/PADToken.artifacts.json" diff --git a/evm/config.ts b/evm/config.ts new file mode 100644 index 0000000..8726ae6 --- /dev/null +++ b/evm/config.ts @@ -0,0 +1,2 @@ +export const PAD_TOKEN_INITIAL_OWNER = + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; diff --git a/evm/contracts/PADToken.sol b/evm/contracts/PADToken.sol new file mode 100644 index 0000000..50b2321 --- /dev/null +++ b/evm/contracts/PADToken.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// Compatible with OpenZeppelin Contracts ^5.0.0 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; + +contract PADToken is ERC20, Ownable, ERC20Permit { + constructor(address initialOwner) + ERC20("PAD Token", "PAD") + Ownable(initialOwner) + ERC20Permit("PAD Token") + {} + + function mint(address to, uint256 amount) public onlyOwner { + _mint(to, amount); + } +} diff --git a/evm/ignition/modules/Main.ts b/evm/ignition/modules/Main.ts new file mode 100644 index 0000000..f8f65c9 --- /dev/null +++ b/evm/ignition/modules/Main.ts @@ -0,0 +1,15 @@ +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; +import { PAD_TOKEN_INITIAL_OWNER } from "../../config"; + +const MainModule = buildModule("MainModule", (m) => { + const initialOwner = m.getParameter( + "padTokenInitialOwner", + PAD_TOKEN_INITIAL_OWNER + ); + + const padToken = m.contract("PADToken", [initialOwner]); + + return { padToken }; +}); + +export default MainModule; diff --git a/evm/package.json b/evm/package.json index 222e4a2..dfa4dd3 100644 --- a/evm/package.json +++ b/evm/package.json @@ -11,6 +11,7 @@ "author": "", "license": "ISC", "dependencies": { + "@openzeppelin/contracts": "^5.0.2", "hardhat": "^2.22.2" }, "devDependencies": { diff --git a/evm/pnpm-lock.yaml b/evm/pnpm-lock.yaml index 73231a6..3351059 100644 --- a/evm/pnpm-lock.yaml +++ b/evm/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@openzeppelin/contracts': + specifier: ^5.0.2 + version: 5.0.2 hardhat: specifier: ^2.22.2 version: 2.22.2(ts-node@10.9.2)(typescript@5.4.5) @@ -844,6 +847,10 @@ packages: '@nomicfoundation/solidity-analyzer-win32-ia32-msvc': 0.1.1 '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.1 + /@openzeppelin/contracts@5.0.2: + resolution: {integrity: sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==} + dev: false + /@scure/base@1.1.6: resolution: {integrity: sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==} diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index d6c9537..c5330af 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -1,18 +1,20 @@ module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, -} + root: true, + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + ], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + // My additions + "@typescript-eslint/ban-ts-comment": "off", + }, +}; diff --git a/frontend/package.json b/frontend/package.json index b639d17..b663a1d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,7 +14,8 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "sonner": "^1.4.41", - "web3": "^4.7.0" + "web3": "^4.7.0", + "zustand": "^4.5.2" }, "devDependencies": { "@tanstack/router-vite-plugin": "^1.28.2", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 8b24db6..ccd15c1 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -20,6 +20,9 @@ dependencies: web3: specifier: ^4.7.0 version: 4.7.0(typescript@5.4.5) + zustand: + specifier: ^4.5.2 + version: 4.5.2(@types/react@18.2.79)(react@18.2.0) devDependencies: '@tanstack/router-vite-plugin': @@ -1026,7 +1029,6 @@ packages: /@types/prop-types@15.7.12: resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} - dev: true /@types/react-dom@18.2.25: resolution: {integrity: sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==} @@ -1039,7 +1041,6 @@ packages: dependencies: '@types/prop-types': 15.7.12 csstype: 3.1.3 - dev: true /@types/semver@7.5.8: resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} @@ -1484,7 +1485,6 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - dev: true /culori@3.3.0: resolution: {integrity: sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==} @@ -3233,3 +3233,23 @@ packages: /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + + /zustand@4.5.2(@types/react@18.2.79)(react@18.2.0): + resolution: {integrity: sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + dependencies: + '@types/react': 18.2.79 + react: 18.2.0 + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false diff --git a/frontend/src/components/ConnectMetamaskButton.tsx b/frontend/src/components/ConnectMetamaskButton.tsx new file mode 100644 index 0000000..d4fc723 --- /dev/null +++ b/frontend/src/components/ConnectMetamaskButton.tsx @@ -0,0 +1,41 @@ +import { useStore } from "../store"; +import { shortenAddress, tryWithToast } from "../utils"; +import { web3 } from "../web3"; + +export function ConnectMetamaskButton() { + const connectedAccount = useStore((state) => state.connectedAccount); + const setConnectedAccount = useStore((state) => state.setConnectedAccount); + const unsetConnectedAccount = useStore( + (state) => state.unsetConnectedAccount + ); + + // https://docs.web3js.org/guides/getting_started/metamask/#react-app + async function connectMetamask() { + await tryWithToast("Connect MetaMask", async () => { + // Request user to connect accounts (MetaMask will prompt) + await window.ethereum.request({ method: "eth_requestAccounts" }); + + // Get the connected accounts + const accounts = await web3.eth.getAccounts(); + + // Show the first connected account in the page + setConnectedAccount(accounts[0]); + }); + } + + function disconnectWallet() { + unsetConnectedAccount(); + } + + return ( + + ); +} diff --git a/frontend/src/evm-output/PADToken.artifacts.json b/frontend/src/evm-output/PADToken.artifacts.json new file mode 100644 index 0000000..518a4c1 --- /dev/null +++ b/frontend/src/evm-output/PADToken.artifacts.json @@ -0,0 +1,632 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "PADToken", + "sourceName": "contracts/PADToken.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "initialOwner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ECDSAInvalidSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "length", + "type": "uint256" + } + ], + "name": "ECDSAInvalidSignatureLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "ECDSAInvalidSignatureS", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "ERC2612ExpiredSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ERC2612InvalidSigner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "currentNonce", + "type": "uint256" + } + ], + "name": "InvalidAccountNonce", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidShortString", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "str", + "type": "string" + } + ], + "name": "StringTooLong", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "EIP712DomainChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "eip712Domain", + "outputs": [ + { + "internalType": "bytes1", + "name": "fields", + "type": "bytes1" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "verifyingContract", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256[]", + "name": "extensions", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x6101606040523480156200001257600080fd5b5060405162002a2938038062002a298339818101604052810190620000389190620004d3565b6040518060400160405280600981526020017f50414420546f6b656e0000000000000000000000000000000000000000000000815250806040518060400160405280600181526020017f3100000000000000000000000000000000000000000000000000000000000000815250836040518060400160405280600981526020017f50414420546f6b656e00000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f504144000000000000000000000000000000000000000000000000000000000081525081600390816200012391906200077f565b5080600490816200013591906200077f565b505050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603620001ad5760006040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600401620001a4919062000877565b60405180910390fd5b620001be816200027760201b60201c565b50620001d56006836200033d60201b90919060201c565b6101208181525050620001f36007826200033d60201b90919060201c565b6101408181525050818051906020012060e08181525050808051906020012061010081815250504660a08181525050620002326200039560201b60201c565b608081815250503073ffffffffffffffffffffffffffffffffffffffff1660c08173ffffffffffffffffffffffffffffffffffffffff16815250505050505062000a72565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600060208351101562000363576200035b83620003f260201b60201c565b90506200038f565b8262000375836200045f60201b60201c565b60000190816200038691906200077f565b5060ff60001b90505b92915050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60e051610100514630604051602001620003d7959493929190620008c0565b60405160208183030381529060405280519060200120905090565b600080829050601f815111156200044257826040517f305a27a9000000000000000000000000000000000000000000000000000000008152600401620004399190620009ac565b60405180910390fd5b805181620004509062000a02565b60001c1760001b915050919050565b6000819050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200049b826200046e565b9050919050565b620004ad816200048e565b8114620004b957600080fd5b50565b600081519050620004cd81620004a2565b92915050565b600060208284031215620004ec57620004eb62000469565b5b6000620004fc84828501620004bc565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200058757607f821691505b6020821081036200059d576200059c6200053f565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620006077fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620005c8565b620006138683620005c8565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620006606200065a62000654846200062b565b62000635565b6200062b565b9050919050565b6000819050919050565b6200067c836200063f565b620006946200068b8262000667565b848454620005d5565b825550505050565b600090565b620006ab6200069c565b620006b881848462000671565b505050565b5b81811015620006e057620006d4600082620006a1565b600181019050620006be565b5050565b601f8211156200072f57620006f981620005a3565b6200070484620005b8565b8101602085101562000714578190505b6200072c6200072385620005b8565b830182620006bd565b50505b505050565b600082821c905092915050565b6000620007546000198460080262000734565b1980831691505092915050565b60006200076f838362000741565b9150826002028217905092915050565b6200078a8262000505565b67ffffffffffffffff811115620007a657620007a562000510565b5b620007b282546200056e565b620007bf828285620006e4565b600060209050601f831160018114620007f75760008415620007e2578287015190505b620007ee858262000761565b8655506200085e565b601f1984166200080786620005a3565b60005b8281101562000831578489015182556001820191506020850194506020810190506200080a565b868310156200085157848901516200084d601f89168262000741565b8355505b6001600288020188555050505b505050505050565b62000871816200048e565b82525050565b60006020820190506200088e600083018462000866565b92915050565b6000819050919050565b620008a98162000894565b82525050565b620008ba816200062b565b82525050565b600060a082019050620008d760008301886200089e565b620008e660208301876200089e565b620008f560408301866200089e565b620009046060830185620008af565b62000913608083018462000866565b9695505050505050565b600082825260208201905092915050565b60005b838110156200094e57808201518184015260208101905062000931565b60008484015250505050565b6000601f19601f8301169050919050565b6000620009788262000505565b6200098481856200091d565b9350620009968185602086016200092e565b620009a1816200095a565b840191505092915050565b60006020820190508181036000830152620009c881846200096b565b905092915050565b600081519050919050565b6000819050602082019050919050565b6000620009f9825162000894565b80915050919050565b600062000a0f82620009d0565b8262000a1b84620009db565b905062000a2881620009eb565b9250602082101562000a6b5762000a667fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83602003600802620005c8565b831692505b5050919050565b60805160a05160c05160e051610100516101205161014051611f5c62000acd6000396000610d8301526000610d48015260006112980152600061127701526000610a7401526000610aca01526000610af30152611f5c6000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c8063715018a6116100a257806395d89b411161007157806395d89b41146102b0578063a9059cbb146102ce578063d505accf146102fe578063dd62ed3e1461031a578063f2fde38b1461034a5761010b565b8063715018a6146102345780637ecebe001461023e57806384b0196e1461026e5780638da5cb5b146102925761010b565b8063313ce567116100de578063313ce567146101ac5780633644e515146101ca57806340c10f19146101e857806370a08231146102045761010b565b806306fdde0314610110578063095ea7b31461012e57806318160ddd1461015e57806323b872dd1461017c575b600080fd5b610118610366565b6040516101259190611785565b60405180910390f35b61014860048036038101906101439190611840565b6103f8565b604051610155919061189b565b60405180910390f35b61016661041b565b60405161017391906118c5565b60405180910390f35b610196600480360381019061019191906118e0565b610425565b6040516101a3919061189b565b60405180910390f35b6101b4610454565b6040516101c1919061194f565b60405180910390f35b6101d261045d565b6040516101df9190611983565b60405180910390f35b61020260048036038101906101fd9190611840565b61046c565b005b61021e6004803603810190610219919061199e565b610482565b60405161022b91906118c5565b60405180910390f35b61023c6104ca565b005b6102586004803603810190610253919061199e565b6104de565b60405161026591906118c5565b60405180910390f35b6102766104f0565b6040516102899796959493929190611ad3565b60405180910390f35b61029a61059a565b6040516102a79190611b57565b60405180910390f35b6102b86105c4565b6040516102c59190611785565b60405180910390f35b6102e860048036038101906102e39190611840565b610656565b6040516102f5919061189b565b60405180910390f35b61031860048036038101906103139190611bca565b610679565b005b610334600480360381019061032f9190611c6c565b6107c1565b60405161034191906118c5565b60405180910390f35b610364600480360381019061035f919061199e565b610848565b005b60606003805461037590611cdb565b80601f01602080910402602001604051908101604052809291908181526020018280546103a190611cdb565b80156103ee5780601f106103c3576101008083540402835291602001916103ee565b820191906000526020600020905b8154815290600101906020018083116103d157829003601f168201915b5050505050905090565b6000806104036108ce565b90506104108185856108d6565b600191505092915050565b6000600254905090565b6000806104306108ce565b905061043d8582856108e8565b61044885858561097c565b60019150509392505050565b60006012905090565b6000610467610a70565b905090565b610474610b27565b61047e8282610bae565b5050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6104d2610b27565b6104dc6000610c30565b565b60006104e982610cf6565b9050919050565b600060608060008060006060610504610d3f565b61050c610d7a565b46306000801b600067ffffffffffffffff81111561052d5761052c611d0c565b5b60405190808252806020026020018201604052801561055b5781602001602082028036833780820191505090505b507f0f00000000000000000000000000000000000000000000000000000000000000959493929190965096509650965096509650965090919293949596565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060600480546105d390611cdb565b80601f01602080910402602001604051908101604052809291908181526020018280546105ff90611cdb565b801561064c5780601f106106215761010080835404028352916020019161064c565b820191906000526020600020905b81548152906001019060200180831161062f57829003601f168201915b5050505050905090565b6000806106616108ce565b905061066e81858561097c565b600191505092915050565b834211156106be57836040517f627913020000000000000000000000000000000000000000000000000000000081526004016106b591906118c5565b60405180910390fd5b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886106ed8c610db5565b8960405160200161070396959493929190611d3b565b604051602081830303815290604052805190602001209050600061072682610e0c565b9050600061073682878787610e26565b90508973ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146107aa57808a6040517f4b800e460000000000000000000000000000000000000000000000000000000081526004016107a1929190611d9c565b60405180910390fd5b6107b58a8a8a6108d6565b50505050505050505050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b610850610b27565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036108c25760006040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526004016108b99190611b57565b60405180910390fd5b6108cb81610c30565b50565b600033905090565b6108e38383836001610e56565b505050565b60006108f484846107c1565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146109765781811015610966578281836040517ffb8f41b200000000000000000000000000000000000000000000000000000000815260040161095d93929190611dc5565b60405180910390fd5b61097584848484036000610e56565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036109ee5760006040517f96c6fd1e0000000000000000000000000000000000000000000000000000000081526004016109e59190611b57565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610a605760006040517fec442f05000000000000000000000000000000000000000000000000000000008152600401610a579190611b57565b60405180910390fd5b610a6b83838361102d565b505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff16148015610aec57507f000000000000000000000000000000000000000000000000000000000000000046145b15610b19577f00000000000000000000000000000000000000000000000000000000000000009050610b24565b610b21611252565b90505b90565b610b2f6108ce565b73ffffffffffffffffffffffffffffffffffffffff16610b4d61059a565b73ffffffffffffffffffffffffffffffffffffffff1614610bac57610b706108ce565b6040517f118cdaa7000000000000000000000000000000000000000000000000000000008152600401610ba39190611b57565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610c205760006040517fec442f05000000000000000000000000000000000000000000000000000000008152600401610c179190611b57565b60405180910390fd5b610c2c6000838361102d565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b6000600860008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6060610d7560067f00000000000000000000000000000000000000000000000000000000000000006112e890919063ffffffff16565b905090565b6060610db060077f00000000000000000000000000000000000000000000000000000000000000006112e890919063ffffffff16565b905090565b6000600860008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000815480929190600101919050559050919050565b6000610e1f610e19610a70565b83611398565b9050919050565b600080600080610e38888888886113d9565b925092509250610e4882826114cd565b829350505050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603610ec85760006040517fe602df05000000000000000000000000000000000000000000000000000000008152600401610ebf9190611b57565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610f3a5760006040517f94280d62000000000000000000000000000000000000000000000000000000008152600401610f319190611b57565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508015611027578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161101e91906118c5565b60405180910390a35b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361107f5780600260008282546110739190611e2b565b92505081905550611152565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561110b578381836040517fe450d38c00000000000000000000000000000000000000000000000000000000815260040161110293929190611dc5565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361119b57806002600082825403925050819055506111e8565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161124591906118c5565b60405180910390a3505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000046306040516020016112cd959493929190611e5f565b60405160208183030381529060405280519060200120905090565b606060ff60001b8314611305576112fe83611631565b9050611392565b81805461131190611cdb565b80601f016020809104026020016040519081016040528092919081815260200182805461133d90611cdb565b801561138a5780601f1061135f5761010080835404028352916020019161138a565b820191906000526020600020905b81548152906001019060200180831161136d57829003601f168201915b505050505090505b92915050565b60006040517f190100000000000000000000000000000000000000000000000000000000000081528360028201528260228201526042812091505092915050565b60008060007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08460001c11156114195760006003859250925092506114c3565b60006001888888886040516000815260200160405260405161143e9493929190611eb2565b6020604051602081039080840390855afa158015611460573d6000803e3d6000fd5b505050602060405103519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036114b457600060016000801b935093509350506114c3565b8060008060001b935093509350505b9450945094915050565b600060038111156114e1576114e0611ef7565b5b8260038111156114f4576114f3611ef7565b5b031561162d576001600381111561150e5761150d611ef7565b5b82600381111561152157611520611ef7565b5b03611558576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600381111561156c5761156b611ef7565b5b82600381111561157f5761157e611ef7565b5b036115c4578060001c6040517ffce698f70000000000000000000000000000000000000000000000000000000081526004016115bb91906118c5565b60405180910390fd5b6003808111156115d7576115d6611ef7565b5b8260038111156115ea576115e9611ef7565b5b0361162c57806040517fd78bce0c0000000000000000000000000000000000000000000000000000000081526004016116239190611983565b60405180910390fd5b5b5050565b6060600061163e836116a5565b90506000602067ffffffffffffffff81111561165d5761165c611d0c565b5b6040519080825280601f01601f19166020018201604052801561168f5781602001600182028036833780820191505090505b5090508181528360208201528092505050919050565b60008060ff8360001c169050601f8111156116ec576040517fb3512b0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80915050919050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561172f578082015181840152602081019050611714565b60008484015250505050565b6000601f19601f8301169050919050565b6000611757826116f5565b6117618185611700565b9350611771818560208601611711565b61177a8161173b565b840191505092915050565b6000602082019050818103600083015261179f818461174c565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006117d7826117ac565b9050919050565b6117e7816117cc565b81146117f257600080fd5b50565b600081359050611804816117de565b92915050565b6000819050919050565b61181d8161180a565b811461182857600080fd5b50565b60008135905061183a81611814565b92915050565b60008060408385031215611857576118566117a7565b5b6000611865858286016117f5565b92505060206118768582860161182b565b9150509250929050565b60008115159050919050565b61189581611880565b82525050565b60006020820190506118b0600083018461188c565b92915050565b6118bf8161180a565b82525050565b60006020820190506118da60008301846118b6565b92915050565b6000806000606084860312156118f9576118f86117a7565b5b6000611907868287016117f5565b9350506020611918868287016117f5565b92505060406119298682870161182b565b9150509250925092565b600060ff82169050919050565b61194981611933565b82525050565b60006020820190506119646000830184611940565b92915050565b6000819050919050565b61197d8161196a565b82525050565b60006020820190506119986000830184611974565b92915050565b6000602082840312156119b4576119b36117a7565b5b60006119c2848285016117f5565b91505092915050565b60007fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b611a00816119cb565b82525050565b611a0f816117cc565b82525050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b611a4a8161180a565b82525050565b6000611a5c8383611a41565b60208301905092915050565b6000602082019050919050565b6000611a8082611a15565b611a8a8185611a20565b9350611a9583611a31565b8060005b83811015611ac6578151611aad8882611a50565b9750611ab883611a68565b925050600181019050611a99565b5085935050505092915050565b600060e082019050611ae8600083018a6119f7565b8181036020830152611afa818961174c565b90508181036040830152611b0e818861174c565b9050611b1d60608301876118b6565b611b2a6080830186611a06565b611b3760a0830185611974565b81810360c0830152611b498184611a75565b905098975050505050505050565b6000602082019050611b6c6000830184611a06565b92915050565b611b7b81611933565b8114611b8657600080fd5b50565b600081359050611b9881611b72565b92915050565b611ba78161196a565b8114611bb257600080fd5b50565b600081359050611bc481611b9e565b92915050565b600080600080600080600060e0888a031215611be957611be86117a7565b5b6000611bf78a828b016117f5565b9750506020611c088a828b016117f5565b9650506040611c198a828b0161182b565b9550506060611c2a8a828b0161182b565b9450506080611c3b8a828b01611b89565b93505060a0611c4c8a828b01611bb5565b92505060c0611c5d8a828b01611bb5565b91505092959891949750929550565b60008060408385031215611c8357611c826117a7565b5b6000611c91858286016117f5565b9250506020611ca2858286016117f5565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680611cf357607f821691505b602082108103611d0657611d05611cac565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600060c082019050611d506000830189611974565b611d5d6020830188611a06565b611d6a6040830187611a06565b611d7760608301866118b6565b611d8460808301856118b6565b611d9160a08301846118b6565b979650505050505050565b6000604082019050611db16000830185611a06565b611dbe6020830184611a06565b9392505050565b6000606082019050611dda6000830186611a06565b611de760208301856118b6565b611df460408301846118b6565b949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000611e368261180a565b9150611e418361180a565b9250828201905080821115611e5957611e58611dfc565b5b92915050565b600060a082019050611e746000830188611974565b611e816020830187611974565b611e8e6040830186611974565b611e9b60608301856118b6565b611ea86080830184611a06565b9695505050505050565b6000608082019050611ec76000830187611974565b611ed46020830186611940565b611ee16040830185611974565b611eee6060830184611974565b95945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea26469706673582212206297b56840db1d0b1cd745ba6d612125a1507b6563b8cbee9e81ca1aed1cdbe764736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061010b5760003560e01c8063715018a6116100a257806395d89b411161007157806395d89b41146102b0578063a9059cbb146102ce578063d505accf146102fe578063dd62ed3e1461031a578063f2fde38b1461034a5761010b565b8063715018a6146102345780637ecebe001461023e57806384b0196e1461026e5780638da5cb5b146102925761010b565b8063313ce567116100de578063313ce567146101ac5780633644e515146101ca57806340c10f19146101e857806370a08231146102045761010b565b806306fdde0314610110578063095ea7b31461012e57806318160ddd1461015e57806323b872dd1461017c575b600080fd5b610118610366565b6040516101259190611785565b60405180910390f35b61014860048036038101906101439190611840565b6103f8565b604051610155919061189b565b60405180910390f35b61016661041b565b60405161017391906118c5565b60405180910390f35b610196600480360381019061019191906118e0565b610425565b6040516101a3919061189b565b60405180910390f35b6101b4610454565b6040516101c1919061194f565b60405180910390f35b6101d261045d565b6040516101df9190611983565b60405180910390f35b61020260048036038101906101fd9190611840565b61046c565b005b61021e6004803603810190610219919061199e565b610482565b60405161022b91906118c5565b60405180910390f35b61023c6104ca565b005b6102586004803603810190610253919061199e565b6104de565b60405161026591906118c5565b60405180910390f35b6102766104f0565b6040516102899796959493929190611ad3565b60405180910390f35b61029a61059a565b6040516102a79190611b57565b60405180910390f35b6102b86105c4565b6040516102c59190611785565b60405180910390f35b6102e860048036038101906102e39190611840565b610656565b6040516102f5919061189b565b60405180910390f35b61031860048036038101906103139190611bca565b610679565b005b610334600480360381019061032f9190611c6c565b6107c1565b60405161034191906118c5565b60405180910390f35b610364600480360381019061035f919061199e565b610848565b005b60606003805461037590611cdb565b80601f01602080910402602001604051908101604052809291908181526020018280546103a190611cdb565b80156103ee5780601f106103c3576101008083540402835291602001916103ee565b820191906000526020600020905b8154815290600101906020018083116103d157829003601f168201915b5050505050905090565b6000806104036108ce565b90506104108185856108d6565b600191505092915050565b6000600254905090565b6000806104306108ce565b905061043d8582856108e8565b61044885858561097c565b60019150509392505050565b60006012905090565b6000610467610a70565b905090565b610474610b27565b61047e8282610bae565b5050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6104d2610b27565b6104dc6000610c30565b565b60006104e982610cf6565b9050919050565b600060608060008060006060610504610d3f565b61050c610d7a565b46306000801b600067ffffffffffffffff81111561052d5761052c611d0c565b5b60405190808252806020026020018201604052801561055b5781602001602082028036833780820191505090505b507f0f00000000000000000000000000000000000000000000000000000000000000959493929190965096509650965096509650965090919293949596565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060600480546105d390611cdb565b80601f01602080910402602001604051908101604052809291908181526020018280546105ff90611cdb565b801561064c5780601f106106215761010080835404028352916020019161064c565b820191906000526020600020905b81548152906001019060200180831161062f57829003601f168201915b5050505050905090565b6000806106616108ce565b905061066e81858561097c565b600191505092915050565b834211156106be57836040517f627913020000000000000000000000000000000000000000000000000000000081526004016106b591906118c5565b60405180910390fd5b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886106ed8c610db5565b8960405160200161070396959493929190611d3b565b604051602081830303815290604052805190602001209050600061072682610e0c565b9050600061073682878787610e26565b90508973ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146107aa57808a6040517f4b800e460000000000000000000000000000000000000000000000000000000081526004016107a1929190611d9c565b60405180910390fd5b6107b58a8a8a6108d6565b50505050505050505050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b610850610b27565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036108c25760006040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526004016108b99190611b57565b60405180910390fd5b6108cb81610c30565b50565b600033905090565b6108e38383836001610e56565b505050565b60006108f484846107c1565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146109765781811015610966578281836040517ffb8f41b200000000000000000000000000000000000000000000000000000000815260040161095d93929190611dc5565b60405180910390fd5b61097584848484036000610e56565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036109ee5760006040517f96c6fd1e0000000000000000000000000000000000000000000000000000000081526004016109e59190611b57565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610a605760006040517fec442f05000000000000000000000000000000000000000000000000000000008152600401610a579190611b57565b60405180910390fd5b610a6b83838361102d565b505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff16148015610aec57507f000000000000000000000000000000000000000000000000000000000000000046145b15610b19577f00000000000000000000000000000000000000000000000000000000000000009050610b24565b610b21611252565b90505b90565b610b2f6108ce565b73ffffffffffffffffffffffffffffffffffffffff16610b4d61059a565b73ffffffffffffffffffffffffffffffffffffffff1614610bac57610b706108ce565b6040517f118cdaa7000000000000000000000000000000000000000000000000000000008152600401610ba39190611b57565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610c205760006040517fec442f05000000000000000000000000000000000000000000000000000000008152600401610c179190611b57565b60405180910390fd5b610c2c6000838361102d565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b6000600860008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6060610d7560067f00000000000000000000000000000000000000000000000000000000000000006112e890919063ffffffff16565b905090565b6060610db060077f00000000000000000000000000000000000000000000000000000000000000006112e890919063ffffffff16565b905090565b6000600860008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000815480929190600101919050559050919050565b6000610e1f610e19610a70565b83611398565b9050919050565b600080600080610e38888888886113d9565b925092509250610e4882826114cd565b829350505050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603610ec85760006040517fe602df05000000000000000000000000000000000000000000000000000000008152600401610ebf9190611b57565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610f3a5760006040517f94280d62000000000000000000000000000000000000000000000000000000008152600401610f319190611b57565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508015611027578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161101e91906118c5565b60405180910390a35b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361107f5780600260008282546110739190611e2b565b92505081905550611152565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561110b578381836040517fe450d38c00000000000000000000000000000000000000000000000000000000815260040161110293929190611dc5565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361119b57806002600082825403925050819055506111e8565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161124591906118c5565b60405180910390a3505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000046306040516020016112cd959493929190611e5f565b60405160208183030381529060405280519060200120905090565b606060ff60001b8314611305576112fe83611631565b9050611392565b81805461131190611cdb565b80601f016020809104026020016040519081016040528092919081815260200182805461133d90611cdb565b801561138a5780601f1061135f5761010080835404028352916020019161138a565b820191906000526020600020905b81548152906001019060200180831161136d57829003601f168201915b505050505090505b92915050565b60006040517f190100000000000000000000000000000000000000000000000000000000000081528360028201528260228201526042812091505092915050565b60008060007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08460001c11156114195760006003859250925092506114c3565b60006001888888886040516000815260200160405260405161143e9493929190611eb2565b6020604051602081039080840390855afa158015611460573d6000803e3d6000fd5b505050602060405103519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036114b457600060016000801b935093509350506114c3565b8060008060001b935093509350505b9450945094915050565b600060038111156114e1576114e0611ef7565b5b8260038111156114f4576114f3611ef7565b5b031561162d576001600381111561150e5761150d611ef7565b5b82600381111561152157611520611ef7565b5b03611558576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600381111561156c5761156b611ef7565b5b82600381111561157f5761157e611ef7565b5b036115c4578060001c6040517ffce698f70000000000000000000000000000000000000000000000000000000081526004016115bb91906118c5565b60405180910390fd5b6003808111156115d7576115d6611ef7565b5b8260038111156115ea576115e9611ef7565b5b0361162c57806040517fd78bce0c0000000000000000000000000000000000000000000000000000000081526004016116239190611983565b60405180910390fd5b5b5050565b6060600061163e836116a5565b90506000602067ffffffffffffffff81111561165d5761165c611d0c565b5b6040519080825280601f01601f19166020018201604052801561168f5781602001600182028036833780820191505090505b5090508181528360208201528092505050919050565b60008060ff8360001c169050601f8111156116ec576040517fb3512b0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80915050919050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561172f578082015181840152602081019050611714565b60008484015250505050565b6000601f19601f8301169050919050565b6000611757826116f5565b6117618185611700565b9350611771818560208601611711565b61177a8161173b565b840191505092915050565b6000602082019050818103600083015261179f818461174c565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006117d7826117ac565b9050919050565b6117e7816117cc565b81146117f257600080fd5b50565b600081359050611804816117de565b92915050565b6000819050919050565b61181d8161180a565b811461182857600080fd5b50565b60008135905061183a81611814565b92915050565b60008060408385031215611857576118566117a7565b5b6000611865858286016117f5565b92505060206118768582860161182b565b9150509250929050565b60008115159050919050565b61189581611880565b82525050565b60006020820190506118b0600083018461188c565b92915050565b6118bf8161180a565b82525050565b60006020820190506118da60008301846118b6565b92915050565b6000806000606084860312156118f9576118f86117a7565b5b6000611907868287016117f5565b9350506020611918868287016117f5565b92505060406119298682870161182b565b9150509250925092565b600060ff82169050919050565b61194981611933565b82525050565b60006020820190506119646000830184611940565b92915050565b6000819050919050565b61197d8161196a565b82525050565b60006020820190506119986000830184611974565b92915050565b6000602082840312156119b4576119b36117a7565b5b60006119c2848285016117f5565b91505092915050565b60007fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b611a00816119cb565b82525050565b611a0f816117cc565b82525050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b611a4a8161180a565b82525050565b6000611a5c8383611a41565b60208301905092915050565b6000602082019050919050565b6000611a8082611a15565b611a8a8185611a20565b9350611a9583611a31565b8060005b83811015611ac6578151611aad8882611a50565b9750611ab883611a68565b925050600181019050611a99565b5085935050505092915050565b600060e082019050611ae8600083018a6119f7565b8181036020830152611afa818961174c565b90508181036040830152611b0e818861174c565b9050611b1d60608301876118b6565b611b2a6080830186611a06565b611b3760a0830185611974565b81810360c0830152611b498184611a75565b905098975050505050505050565b6000602082019050611b6c6000830184611a06565b92915050565b611b7b81611933565b8114611b8657600080fd5b50565b600081359050611b9881611b72565b92915050565b611ba78161196a565b8114611bb257600080fd5b50565b600081359050611bc481611b9e565b92915050565b600080600080600080600060e0888a031215611be957611be86117a7565b5b6000611bf78a828b016117f5565b9750506020611c088a828b016117f5565b9650506040611c198a828b0161182b565b9550506060611c2a8a828b0161182b565b9450506080611c3b8a828b01611b89565b93505060a0611c4c8a828b01611bb5565b92505060c0611c5d8a828b01611bb5565b91505092959891949750929550565b60008060408385031215611c8357611c826117a7565b5b6000611c91858286016117f5565b9250506020611ca2858286016117f5565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680611cf357607f821691505b602082108103611d0657611d05611cac565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600060c082019050611d506000830189611974565b611d5d6020830188611a06565b611d6a6040830187611a06565b611d7760608301866118b6565b611d8460808301856118b6565b611d9160a08301846118b6565b979650505050505050565b6000604082019050611db16000830185611a06565b611dbe6020830184611a06565b9392505050565b6000606082019050611dda6000830186611a06565b611de760208301856118b6565b611df460408301846118b6565b949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000611e368261180a565b9150611e418361180a565b9250828201905080821115611e5957611e58611dfc565b5b92915050565b600060a082019050611e746000830188611974565b611e816020830187611974565b611e8e6040830186611974565b611e9b60608301856118b6565b611ea86080830184611a06565b9695505050505050565b6000608082019050611ec76000830187611974565b611ed46020830186611940565b611ee16040830185611974565b611eee6060830184611974565b95945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea26469706673582212206297b56840db1d0b1cd745ba6d612125a1507b6563b8cbee9e81ca1aed1cdbe764736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} \ No newline at end of file diff --git a/frontend/src/evm-output/deployed_addresses.json b/frontend/src/evm-output/deployed_addresses.json index a3c2438..cd3767e 100644 --- a/frontend/src/evm-output/deployed_addresses.json +++ b/frontend/src/evm-output/deployed_addresses.json @@ -1,3 +1,4 @@ { - "MessageBoxModule#MessageBox": "0x5FbDB2315678afecb367f032d93F642f64180aa3" + "MessageBoxModule#MessageBox": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "MainModule#PADToken": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" } diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index 6f07e43..ec3fbad 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -16,12 +16,18 @@ import { Route as rootRoute } from './routes/__root' // Create Virtual Routes +const PadTokenLazyImport = createFileRoute('/pad-token')() const MessageBoxLazyImport = createFileRoute('/message-box')() const AboutLazyImport = createFileRoute('/about')() const IndexLazyImport = createFileRoute('/')() // Create/Update Routes +const PadTokenLazyRoute = PadTokenLazyImport.update({ + path: '/pad-token', + getParentRoute: () => rootRoute, +} as any).lazy(() => import('./routes/pad-token.lazy').then((d) => d.Route)) + const MessageBoxLazyRoute = MessageBoxLazyImport.update({ path: '/message-box', getParentRoute: () => rootRoute, @@ -53,6 +59,10 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MessageBoxLazyImport parentRoute: typeof rootRoute } + '/pad-token': { + preLoaderRoute: typeof PadTokenLazyImport + parentRoute: typeof rootRoute + } } } @@ -62,6 +72,7 @@ export const routeTree = rootRoute.addChildren([ IndexLazyRoute, AboutLazyRoute, MessageBoxLazyRoute, + PadTokenLazyRoute, ]) /* prettier-ignore-end */ diff --git a/frontend/src/routes/__root.tsx b/frontend/src/routes/__root.tsx index 6add413..2e1d31b 100644 --- a/frontend/src/routes/__root.tsx +++ b/frontend/src/routes/__root.tsx @@ -1,5 +1,6 @@ import { createRootRoute, Link, Outlet } from "@tanstack/react-router"; import { Toaster } from "sonner"; +import { ConnectMetamaskButton } from "../components/ConnectMetamaskButton"; export const Route = createRootRoute({ component: RootLayout, @@ -8,20 +9,26 @@ export const Route = createRootRoute({ function RootLayout() { return ( <> -
- OsiPad™ -
-
- - Home - - - About - - - Message Box - +
+
+ OsiPad™ +
+
+ + Home + + + About + + + Message Box + + + PAD Token + +
+
diff --git a/frontend/src/routes/message-box.lazy.tsx b/frontend/src/routes/message-box.lazy.tsx index e6afacc..0f5e03c 100644 --- a/frontend/src/routes/message-box.lazy.tsx +++ b/frontend/src/routes/message-box.lazy.tsx @@ -1,31 +1,18 @@ import { createLazyFileRoute } from "@tanstack/react-router"; import { useState } from "react"; -import { tryWithToast } from "../utils"; -import { messageBox, web3 } from "../web3"; +import { useStore } from "../store"; +import { toastError, tryWithToast } from "../utils"; +import { messageBox } from "../web3"; export const Route = createLazyFileRoute("/message-box")({ component: MessageBoxPage, }); function MessageBoxPage() { - const [connectedAccount, setConnectedAccount] = useState("(nothingness)"); + const connectedAccount = useStore((state) => state.connectedAccount); const [message, setMessage] = useState("(nothingness)"); const [newMessage, setNewMessage] = useState(""); - // https://docs.web3js.org/guides/getting_started/metamask/#react-app - async function connectMetamask() { - await tryWithToast("Connect MetaMask", async () => { - // Request user to connect accounts (MetaMask will prompt) - await window.ethereum.request({ method: "eth_requestAccounts" }); - - // Get the connected accounts - const accounts = await web3.eth.getAccounts(); - - // Show the first connected account in the page - setConnectedAccount(accounts[0]); - }); - } - async function getMessage() { await tryWithToast("Get Message", async () => { const _message: string = await messageBox.methods.getMessage().call(); @@ -34,6 +21,11 @@ function MessageBoxPage() { } async function _setMessage() { + if (!connectedAccount) { + toastError("Set Message", "Connect your wallet first."); + return; + } + await tryWithToast("Set Message", async () => { await messageBox.methods .setMessage(newMessage) @@ -43,24 +35,6 @@ function MessageBoxPage() { return (
-
-
-

✨Essentials✨

- - -
- Connected account address:{" "} - {connectedAccount} -
-
-
-

Get Message

diff --git a/frontend/src/routes/pad-token.lazy.tsx b/frontend/src/routes/pad-token.lazy.tsx new file mode 100644 index 0000000..e1dc3f4 --- /dev/null +++ b/frontend/src/routes/pad-token.lazy.tsx @@ -0,0 +1,123 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import { useState } from "react"; +import type { Numbers } from "web3"; +import { useStore } from "../store"; +import { getFormInputValue, toastError, tryWithToast } from "../utils"; +import { padToken, web3 } from "../web3"; + +export const Route = createLazyFileRoute("/pad-token")({ + component: PadTokenPage, +}); + +function PadTokenPage() { + const connectedAccount = useStore((state) => state.connectedAccount); + const [balance, setBalance] = useState("(nothingness)"); + + const onBalanceOfFormSubmit: React.FormEventHandler = async ( + event + ) => { + event.preventDefault(); + + const address = getFormInputValue( + event.target as HTMLFormElement, + "address" + ).trim(); + if (!address) { + toastError("Balance Of", "Enter an address."); + return; + } + + await tryWithToast("Balance Of", async () => { + const balanceBN: Numbers = await padToken.methods + .balanceOf(address) + .call(); + setBalance(web3.utils.fromWei(balanceBN, "ether")); + }); + }; + + const onMintFormSubmit: React.FormEventHandler = async ( + event + ) => { + event.preventDefault(); + + if (!connectedAccount) { + toastError("Mint", "Connect your wallet first."); + return; + } + + const to = getFormInputValue(event.target as HTMLFormElement, "to").trim(); + if (!to) { + toastError("Mint", "Enter a target address."); + return; + } + + const amount = getFormInputValue( + event.target as HTMLFormElement, + "amount" + ).trim(); + if (!amount) { + toastError("Mint", "Enter an amount."); + return; + } + + await tryWithToast("Mint", async () => { + await padToken.methods + .mint(to, web3.utils.toWei(amount, "ether")) + .send({ from: connectedAccount }); + }); + }; + + return ( +
+
+
+

Balance Of

+
+ + +
+ Balance: {balance} PAD +
+
+
+
+ +
+
+

Mint

+
+ + + +
+
+
+
+ ); +} diff --git a/frontend/src/store.tsx b/frontend/src/store.tsx new file mode 100644 index 0000000..92adf15 --- /dev/null +++ b/frontend/src/store.tsx @@ -0,0 +1,13 @@ +import { create } from "zustand"; + +interface State { + connectedAccount: string | null; + setConnectedAccount: (newAccount: string) => void; + unsetConnectedAccount: () => void; +} + +export const useStore = create()((set) => ({ + connectedAccount: null, + setConnectedAccount: (newAccount) => set({ connectedAccount: newAccount }), + unsetConnectedAccount: () => set({ connectedAccount: null }), +})); diff --git a/frontend/src/utils.ts b/frontend/src/utils.ts index dc3bec0..197e78b 100644 --- a/frontend/src/utils.ts +++ b/frontend/src/utils.ts @@ -1,21 +1,41 @@ import { toast } from "sonner"; +export function toastSuccess(taskName: string) { + toast.success(`${taskName}: Success`, { + className: "border-none bg-green-100", + }); +} + +export function toastError(taskName: string, errorMessage: string) { + toast.error(`${taskName}: Error`, { + description: errorMessage, + className: "border-none bg-red-100", + }); +} + export async function tryWithToast( taskName: string, taskFn: () => Promise ) { try { await taskFn(); - toast.success(`${taskName}: Success`, { - className: "border-none bg-green-100", - }); + toastSuccess(taskName); } catch (error: any) { console.error(error); - toast.error(`${taskName}: Error`, { - description: - error.message || - "No error message. Check the DevTools console for details.", - className: "border-none bg-red-100", - }); + toastError( + taskName, + error.message || + "No error message. Check the DevTools console for details." + ); } } + +export function shortenAddress(address: string) { + return address.slice(0, 6) + "..." + address.slice(-4); +} + +export function getFormInputValue(form: HTMLFormElement, name: string) { + // @ts-expect-error + const input = form.elements[name] as HTMLInputElement; + return input.value; +} diff --git a/frontend/src/web3.ts b/frontend/src/web3.ts index 76a08ef..de57eeb 100644 --- a/frontend/src/web3.ts +++ b/frontend/src/web3.ts @@ -1,5 +1,6 @@ import { Web3 } from "web3"; import messageBoxArtifacts from "./evm-output/MessageBox.artifacts.json"; +import padTokenArtifacts from "./evm-output/PADToken.artifacts.json"; import deployedAddresses from "./evm-output/deployed_addresses.json"; export let web3: Web3; @@ -17,3 +18,8 @@ export const messageBox = new web3!.eth.Contract( messageBoxArtifacts.abi, deployedAddresses["MessageBoxModule#MessageBox"] ); + +export const padToken = new web3!.eth.Contract( + padTokenArtifacts.abi, + deployedAddresses["MainModule#PADToken"] +);