zhereh-frontend

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 4738e61f88bb928c048d15e07ddd074838b3fde7
parent a2975206dc3a1c77df5b1b35ede95f3f122073c3
Author: William Muli <willi.wambu@gmail.com>
Date:   Thu, 15 Jun 2023 23:40:10 +0300

Add chain configuration

Diffstat:
A.env.example | 7+++++++
Mpackage-lock.json | 523++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mpackage.json | 8+++++++-
Msrc/app.css | 10++++++++--
Asrc/client.ts | 13+++++++++++++
Asrc/components/Nav.svelte | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/NetworkForm.svelte | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/components/nav/nav.svelte | 46----------------------------------------------
Asrc/global.d.ts | 8++++++++
Msrc/routes/+layout.svelte | 132++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/routes/+page.svelte | 26++++++++++++--------------
Msrc/routes/+page.ts | 4+---
Asrc/shared/erc20-token-abi.ts | 493+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/shared/localstorage-keys.ts | 5+++++
Asrc/shared/token-vote-abi.ts | 523+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/shared/types.ts | 5+++++
Asrc/store.ts | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtailwind.config.js | 19++++++++++++++++++-
18 files changed, 2088 insertions(+), 74 deletions(-)

diff --git a/.env.example b/.env.example @@ -0,0 +1,6 @@ +# Public +PUBLIC_RPC_URL=http://localhost:8545 +PUBLIC_CHAIN_ID=1337 +PUBLIC_DECIMALS=18 +PUBLIC_VOTE_CONTRACT_ADDRESS=0xa170d45e0e34cb5b72736dfa6d1a30e83d7f0d0f32fc79d4e2ab135be4482348 +PUBLIC_ERC20_CONTRACT_ADDRESS=0xefd6e2143651dca5f836bdf27debc773139037a4 +\ No newline at end of file diff --git a/package-lock.json b/package-lock.json @@ -7,6 +7,11 @@ "": { "name": "frontend", "version": "0.0.1", + "dependencies": { + "@wagmi/chains": "^1.1.0", + "ethers": "^6.5.1", + "viem": "^1.0.7" + }, "devDependencies": { "@fontsource/fira-mono": "^4.5.10", "@neoconfetti/svelte": "^1.0.0", @@ -16,6 +21,7 @@ "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", "autoprefixer": "^10.4.14", + "daisyui": "^3.1.0", "eslint": "^8.28.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-svelte": "^2.26.0", @@ -31,6 +37,11 @@ "vitest": "^0.25.3" } }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.9.2.tgz", + "integrity": "sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg==" + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -550,6 +561,53 @@ "integrity": "sha512-SmksyaJAdSlMa9cTidVSIqYo1qti+WTsviNDwgjNVm+KQ3DRP2Df9umDIzC4vCcpEYY+chQe0i2IKnLw03AT8Q==", "dev": true }, + "node_modules/@noble/curves": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", + "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "1.3.0" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", + "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@noble/hashes": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", + "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -591,6 +649,70 @@ "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", "dev": true }, + "node_modules/@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@scure/bip32": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.0.tgz", + "integrity": "sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/curves": "~1.0.0", + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.0.tgz", + "integrity": "sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@sveltejs/adapter-auto": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-2.1.0.tgz", @@ -907,6 +1029,43 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@wagmi/chains": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@wagmi/chains/-/chains-1.1.0.tgz", + "integrity": "sha512-pWZlxBk0Ql8E7DV8DwqlbBpOyUdaG9UDlQPBxJNALuEK1I0tbQ3AVvSDnlsEIt06UPmPo5o27gzs3hwPQ/A+UA==", + "funding": [ + { + "type": "gitcoin", + "url": "https://wagmi.sh/gitcoin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/wagmi-dev" + } + ], + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/abitype": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-0.8.7.tgz", + "integrity": "sha512-wQ7hV8Yg/yKmGyFpqrNZufCxbszDe5es4AZGYPBitocfSqXtjrTG9JMWFcc4N30ukl2ve48aBTwt7NJxVQdU3w==", + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.19.1" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", @@ -937,6 +1096,11 @@ "node": ">=0.4.0" } }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==" + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1287,6 +1451,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -1325,6 +1495,16 @@ "node": ">= 8" } }, + "node_modules/css-selector-tokenizer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", + "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -1337,6 +1517,28 @@ "node": ">=4" } }, + "node_modules/daisyui": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.1.0.tgz", + "integrity": "sha512-G4dz/bRZVvlhQ/FtezXSg0rXOXzDJ0PcnMqeLSwCYNWXxf46fNJ8LWeV5qwsYOdJbXiXoZLnwyy+BsNoWZ+Bjg==", + "dev": true, + "dependencies": { + "colord": "^2.9", + "css-selector-tokenizer": "^0.8", + "postcss-js": "^4", + "tailwindcss": "^3" + }, + "engines": { + "node": ">=16.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/daisyui" + }, + "peerDependencies": { + "postcss": "^8" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1735,6 +1937,43 @@ "node": ">=0.10.0" } }, + "node_modules/ethers": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.5.1.tgz", + "integrity": "sha512-jDpCnUGcyn39hnRUEtrulDZOtJcIPEz4Whccl3N9qhwdLsn1ELuDM9TgGgGJq6ph0p8/Uri+Wezmo/r69E+xkA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.9.2", + "@noble/hashes": "1.1.2", + "@noble/secp256k1": "1.7.1", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1781,6 +2020,12 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -2144,6 +2389,14 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/jiti": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", @@ -3597,7 +3850,6 @@ "version": "5.1.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", - "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3663,6 +3915,61 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/viem": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/viem/-/viem-1.0.7.tgz", + "integrity": "sha512-P/T2Zn8+V74kxO2e2NdhNR/MBtldovssigvc36+7g3DgTeETKnSS1fMQdBl0jE+Atlal+M6cuJ2HcFY7U45o0Q==", + "dependencies": { + "@adraffy/ens-normalize": "1.9.0", + "@noble/curves": "1.0.0", + "@noble/hashes": "1.3.0", + "@scure/bip32": "1.3.0", + "@scure/bip39": "1.2.0", + "@wagmi/chains": "1.1.0", + "abitype": "0.8.7", + "isomorphic-ws": "5.0.0", + "ws": "8.12.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + } + }, + "node_modules/viem/node_modules/@adraffy/ens-normalize": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.9.0.tgz", + "integrity": "sha512-iowxq3U30sghZotgl4s/oJRci6WPBfNO5YYgk2cIOMCHr3LeGPcsZjCEr+33Q4N+oV3OABDAtA+pyvWjbvBifQ==" + }, + "node_modules/viem/node_modules/@noble/hashes": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", + "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/viem/node_modules/ws": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/vite": { "version": "4.3.9", "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", @@ -3810,6 +4117,26 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -3839,6 +4166,11 @@ } }, "dependencies": { + "@adraffy/ens-normalize": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.9.2.tgz", + "integrity": "sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg==" + }, "@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -4119,6 +4451,31 @@ "integrity": "sha512-SmksyaJAdSlMa9cTidVSIqYo1qti+WTsviNDwgjNVm+KQ3DRP2Df9umDIzC4vCcpEYY+chQe0i2IKnLw03AT8Q==", "dev": true }, + "@noble/curves": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", + "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", + "requires": { + "@noble/hashes": "1.3.0" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", + "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==" + } + } + }, + "@noble/hashes": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", + "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==" + }, + "@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4151,6 +4508,44 @@ "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", "dev": true }, + "@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==" + }, + "@scure/bip32": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.0.tgz", + "integrity": "sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==", + "requires": { + "@noble/curves": "~1.0.0", + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + } + } + }, + "@scure/bip39": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.0.tgz", + "integrity": "sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==", + "requires": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + } + } + }, "@sveltejs/adapter-auto": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-2.1.0.tgz", @@ -4349,6 +4744,18 @@ "eslint-visitor-keys": "^3.3.0" } }, + "@wagmi/chains": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@wagmi/chains/-/chains-1.1.0.tgz", + "integrity": "sha512-pWZlxBk0Ql8E7DV8DwqlbBpOyUdaG9UDlQPBxJNALuEK1I0tbQ3AVvSDnlsEIt06UPmPo5o27gzs3hwPQ/A+UA==", + "requires": {} + }, + "abitype": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-0.8.7.tgz", + "integrity": "sha512-wQ7hV8Yg/yKmGyFpqrNZufCxbszDe5es4AZGYPBitocfSqXtjrTG9JMWFcc4N30ukl2ve48aBTwt7NJxVQdU3w==", + "requires": {} + }, "acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", @@ -4368,6 +4775,11 @@ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true }, + "aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==" + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -4598,6 +5010,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, "commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -4627,12 +5045,34 @@ "which": "^2.0.1" } }, + "css-selector-tokenizer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", + "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true }, + "daisyui": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.1.0.tgz", + "integrity": "sha512-G4dz/bRZVvlhQ/FtezXSg0rXOXzDJ0PcnMqeLSwCYNWXxf46fNJ8LWeV5qwsYOdJbXiXoZLnwyy+BsNoWZ+Bjg==", + "dev": true, + "requires": { + "colord": "^2.9", + "css-selector-tokenizer": "^0.8", + "postcss-js": "^4", + "tailwindcss": "^3" + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4927,6 +5367,32 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "ethers": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.5.1.tgz", + "integrity": "sha512-jDpCnUGcyn39hnRUEtrulDZOtJcIPEz4Whccl3N9qhwdLsn1ELuDM9TgGgGJq6ph0p8/Uri+Wezmo/r69E+xkA==", + "requires": { + "@adraffy/ens-normalize": "1.9.2", + "@noble/hashes": "1.1.2", + "@noble/secp256k1": "1.7.1", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.5.0" + }, + "dependencies": { + "@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4969,6 +5435,12 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -5239,6 +5711,12 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "requires": {} + }, "jiti": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", @@ -6198,8 +6676,7 @@ "typescript": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", - "dev": true + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==" }, "undici": { "version": "5.22.1", @@ -6235,6 +6712,40 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "viem": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/viem/-/viem-1.0.7.tgz", + "integrity": "sha512-P/T2Zn8+V74kxO2e2NdhNR/MBtldovssigvc36+7g3DgTeETKnSS1fMQdBl0jE+Atlal+M6cuJ2HcFY7U45o0Q==", + "requires": { + "@adraffy/ens-normalize": "1.9.0", + "@noble/curves": "1.0.0", + "@noble/hashes": "1.3.0", + "@scure/bip32": "1.3.0", + "@scure/bip39": "1.2.0", + "@wagmi/chains": "1.1.0", + "abitype": "0.8.7", + "isomorphic-ws": "5.0.0", + "ws": "8.12.0" + }, + "dependencies": { + "@adraffy/ens-normalize": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.9.0.tgz", + "integrity": "sha512-iowxq3U30sghZotgl4s/oJRci6WPBfNO5YYgk2cIOMCHr3LeGPcsZjCEr+33Q4N+oV3OABDAtA+pyvWjbvBifQ==" + }, + "@noble/hashes": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", + "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==" + }, + "ws": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "requires": {} + } + } + }, "vite": { "version": "4.3.9", "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", @@ -6297,6 +6808,12 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "requires": {} + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json @@ -20,6 +20,7 @@ "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", "autoprefixer": "^10.4.14", + "daisyui": "^3.1.0", "eslint": "^8.28.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-svelte": "^2.26.0", @@ -34,5 +35,10 @@ "vite": "^4.3.0", "vitest": "^0.25.3" }, - "type": "module" + "type": "module", + "dependencies": { + "@wagmi/chains": "^1.1.0", + "ethers": "^6.5.1", + "viem": "^1.0.7" + } } diff --git a/src/app.css b/src/app.css @@ -1,4 +1,11 @@ @import url("https://fonts.googleapis.com/css2?family=Poppins&display=swap"); @tailwind base; @tailwind components; -@tailwind utilities; -\ No newline at end of file +@tailwind utilities; + +body { + background: linear-gradient(180deg,#fff 22%,#d7caec 100%); + overflow: scroll; + overscroll-behavior-y: none; +} + diff --git a/src/client.ts b/src/client.ts @@ -0,0 +1,12 @@ +import { createPublicClient, createWalletClient, custom, http } from 'viem' +import { localhost } from '@wagmi/chains' + +export const walletClient = () => createWalletClient({ + chain: localhost, + transport: custom(window.ethereum) +}) + +export const publicClient = () => createPublicClient({ + chain: localhost, + transport: http() +}) +\ No newline at end of file diff --git a/src/components/Nav.svelte b/src/components/Nav.svelte @@ -0,0 +1,50 @@ +<script lang="ts"> + import { NavOptions } from "../options/nav-options"; +</script> + +<nav class="flex items-center justify-between flex-wrap bg-black p-6"> + <div class="flex items-center flex-shrink-0 text-white mr-6"> + <svg + class="fill-current h-8 w-8 mr-2" + width="54" + height="54" + viewBox="0 0 54 54" + xmlns="http://www.w3.org/2000/svg" + ><path + d="M13.5 22.1c1.8-7.2 6.3-10.8 13.5-10.8 10.8 0 12.15 8.1 17.55 9.45 3.6.9 6.75-.45 9.45-4.05-1.8 7.2-6.3 10.8-13.5 10.8-10.8 0-12.15-8.1-17.55-9.45-3.6-.9-6.75.45-9.45 4.05zM0 38.3c1.8-7.2 6.3-10.8 13.5-10.8 10.8 0 12.15 8.1 17.55 9.45 3.6.9 6.75-.45 9.45-4.05-1.8 7.2-6.3 10.8-13.5 10.8-10.8 0-12.15-8.1-17.55-9.45-3.6-.9-6.75.45-9.45 4.05z" + /></svg + > + <span class="font-semibold text-xl tracking-tight">EVM Token Vote</span> + </div> + <div class="block lg:hidden"> + <button + class="flex items-center px-3 py-2 border rounded text-white border-teal-400 hover:text-white hover:border-white" + > + <svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" + ><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" /></svg + > + </button> + </div> + <div class="w-full block flex-grow lg:flex lg:items-center lg:w-auto lg:ml-20"> + <div class="text-sm lg:flex-grow"> + {#each NavOptions as { url, name} } + <a + href={url} + class="block mt-4 lg:inline-block lg:mt-0 text-gray-400 font-bold hover:text-white hover:text-md mr-4" + > + {name} + </a> + {/each} + </div> + <div> + <button + class="inline-block text-sm px-4 py-2 leading-none rounded text-white bg-emerald-500 hover:text-teal-500 hover:bg-emerald-600 mt-4 lg:mt-0" + on:click + on:keydown + > + Connect wallet + </button + > + </div> + </div> +</nav> diff --git a/src/components/NetworkForm.svelte b/src/components/NetworkForm.svelte @@ -0,0 +1,213 @@ +<script lang="ts"> + import { numberToHex } from 'viem' + import type { Chain } from '@wagmi/chains' + import { configuredChain, votingConfig } from '../store' + $configuredChain + $votingConfig + + type ConfigFormData = { + rpcEndpoint: string + chainName: string + currencyName: string + currencySymbol: string + chainId: number + decimals: number + contractAddress: string + erc20ContractAddress: string + } + + let configData: ConfigFormData = { + rpcEndpoint: $configuredChain.rpcUrls.default.http[0], + chainName: $configuredChain.name, + currencyName: $configuredChain.nativeCurrency.name, + currencySymbol: $configuredChain.nativeCurrency.symbol, + chainId: $configuredChain.id, + decimals: $configuredChain.nativeCurrency.decimals, + contractAddress: $votingConfig.contractAddress, + erc20ContractAddress: $votingConfig.erc20ContractAddress + } + + const onSubmit = async () => { + await addNetworkToWallet(configData) + } + + async function addNetworkToWallet(configData: ConfigFormData) { + if (!window.ethereum) alert('Please install a wallet to continue') + try { + updateConfiguredChain() + votingConfig.updateVotingConfig({ + contractAddress: configData.contractAddress, + erc20ContractAddress: configData.erc20ContractAddress + }) + + await window.ethereum.request({ + method: 'wallet_addEthereumChain', + params: [ + { + chainId: numberToHex(configData.chainId), + rpcUrls: [configData.rpcEndpoint], + chainName: configData.chainName, + nativeCurrency: { + name: configData.currencyName, + symbol: configData.currencySymbol, + decimals: configData.decimals + }, + blockExplorerUrls: ['https://example.com'] + } + ] + }) + } catch (error) { + console.log(error) + } + } + + const updateConfiguredChain = () => { + const updatedChain: Chain = { + id: configData.chainId, + name: configData.chainName, + rpcUrls: { + default: { + http: [configData.rpcEndpoint], + webSocket: undefined + }, + public: { + http: [], + webSocket: undefined + } + }, + nativeCurrency: { + name: configData.currencyName, + symbol: configData.currencySymbol, + decimals: configData.decimals + }, + network: $configuredChain.name + } + configuredChain.updateChain(updatedChain) + } +</script> + +<div class="flex justify-center items-center w-full py-10"> + <div class="flex w-full items-center justify-center"> + <div class="card w-full md:w-2/5 m-2 md:m-0 shadow-xl bg-white"> + <div class="card-body"> + <h2 class="text-gray-900 text-center w-full font-semibold text-xl">Network Details</h2> + <form + on:submit|preventDefault={onSubmit} + class="flex flex-col justify-center w-full flex-1" + > + <div class="form-control w-full"> + <label for="rpcUrl" class="label"> + <span class="label-text text-gray-800">RPC endpoint</span> + </label> + <input + name="rpcUrl" + type="url" + placeholder="Enter RPC endpoint" + class="input input-bordered w-full" + bind:value={configData.rpcEndpoint} + /> + </div> + + <div class="flex flex-col md:flex-row w-full gap-3 mt-3"> + <div class="form-control w-full"> + <label for="chainName" class="label"> + <span class="label-text text-gray-800">Chain Name</span> + </label> + <input + name="chainName" + type="text" + placeholder="Enter chain name" + class="input input-bordered w-full" + bind:value={configData.chainName} + /> + </div> + + <div class="form-control w-full"> + <label for="currencyName" class="label"> + <span class="label-text text-gray-800">Currency Name</span> + </label> + <input + name="currencyName" + type="text" + placeholder="Enter currency name" + class="input input-bordered w-full" + bind:value={configData.currencyName} + /> + </div> + </div> + + <div class="flex flex-col md:flex-row w-full gap-3 mt-3"> + <div class="form-control w-full"> + <label for="currencySymbol" class="label"> + <span class="label-text text-gray-800">Currency symbol</span> + </label> + <input + name="currencySymbol" + type="text" + placeholder="Enter currency symbol(2-6 characters)" + class="input input-bordered w-full" + minlength="2" + maxlength="6" + bind:value={configData.currencySymbol} + /> + </div> + + <div class="form-control w-full"> + <label for="chainId" class="label"> + <span class="label-text text-gray-800">Chain ID</span> + </label> + <input + name="chainId" + type="number" + placeholder="Enter chainId" + class="input input-bordered w-full" + bind:value={configData.chainId} + /> + </div> + <div class="form-control w-full"> + <label for="chainId" class="label"> + <span class="label-text text-gray-800">Decimals</span> + </label> + <input + name="chainId" + type="number" + placeholder="Enter decimals" + class="input input-bordered w-full" + bind:value={configData.decimals} + /> + </div> + </div> + + <div class="form-control w-full mt-3"> + <label for="contractAddress" class="label"> + <span class="label-text text-gray-800">Contract address</span> + </label> + <input + name="contractAddress" + type="text" + placeholder="Enter voting contract address" + class="input input-bordered w-full" + bind:value={configData.contractAddress} + /> + </div> + + <div class="form-control w-full mt-3"> + <label for="erc20ContractAddress" class="label"> + <span class="label-text text-gray-800">ERC-20 Contract address</span> + </label> + <input + name="erc20ContractAddress" + type="text" + placeholder="Enter ERC-20 contract address" + class="input input-bordered w-full" + bind:value={configData.erc20ContractAddress} + /> + </div> + <button type="submit" class="btn btn-primary w-40 self-center mt-6 normal-case text-white" + >Add to wallet</button + > + </form> + </div> + </div> + </div> +</div> diff --git a/src/components/nav/nav.svelte b/src/components/nav/nav.svelte @@ -1,46 +0,0 @@ -<script lang="ts"> - import { NavOptions } from "../../options/nav-options"; -</script> - -<nav class="flex items-center justify-between flex-wrap bg-black p-6"> - <div class="flex items-center flex-shrink-0 text-white mr-6"> - <svg - class="fill-current h-8 w-8 mr-2" - width="54" - height="54" - viewBox="0 0 54 54" - xmlns="http://www.w3.org/2000/svg" - ><path - d="M13.5 22.1c1.8-7.2 6.3-10.8 13.5-10.8 10.8 0 12.15 8.1 17.55 9.45 3.6.9 6.75-.45 9.45-4.05-1.8 7.2-6.3 10.8-13.5 10.8-10.8 0-12.15-8.1-17.55-9.45-3.6-.9-6.75.45-9.45 4.05zM0 38.3c1.8-7.2 6.3-10.8 13.5-10.8 10.8 0 12.15 8.1 17.55 9.45 3.6.9 6.75-.45 9.45-4.05-1.8 7.2-6.3 10.8-13.5 10.8-10.8 0-12.15-8.1-17.55-9.45-3.6-.9-6.75.45-9.45 4.05z" - /></svg - > - <span class="font-semibold text-xl tracking-tight">EVM Token Vote</span> - </div> - <div class="block lg:hidden"> - <button - class="flex items-center px-3 py-2 border rounded text-white border-teal-400 hover:text-white hover:border-white" - > - <svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" - ><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" /></svg - > - </button> - </div> - <div class="w-full block flex-grow lg:flex lg:items-center lg:w-auto lg:ml-20"> - <div class="text-sm lg:flex-grow"> - {#each NavOptions as { url, name} } - <a - href={url} - class="block mt-4 lg:inline-block lg:mt-0 text-gray-400 font-bold hover:text-white hover:text-md mr-4" - > - {name} - </a> - {/each} - </div> - <div> - <button - class="inline-block text-sm px-4 py-2 leading-none rounded text-white bg-emerald-500 hover:text-teal-500 hover:bg-emerald-600 mt-4 lg:mt-0" - >Connect wallet</button - > - </div> - </div> -</nav> diff --git a/src/global.d.ts b/src/global.d.ts @@ -0,0 +1,7 @@ +export {} + +declare global { + interface Window { + ethereum: EthereumProvider + } +} +\ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte @@ -1,5 +1,130 @@ -<script> - import "../app.css"; +<script lang="ts"> + import { onMount } from 'svelte' + import { formatEther, getContract, parseEther, type RequestAddressesReturnType, stringToHex } from 'viem' + import { walletClient, publicClient } from '../client' + import Nav from '../components/Nav.svelte' + import '../app.css' + import { PUBLIC_ERC20_CONTRACT_ADDRESS, PUBLIC_VOTE_CONTRACT_ADDRESS } from '$env/static/public' + import { erc20TokenAbi, gitableAbi } from '../shared/erc20-token-abi' + import { voteContractAbi } from '../shared/token-vote-abi' + import { configuredChain, votingConfig } from '../store' + + let userAddress: `0x${string}` | undefined + let balance: bigint | undefined + let isConnected: boolean = false + // $configuredChain + + // console.log($configuredChain) + configuredChain.subscribe(value => console.log(value)) + votingConfig.subscribe(value => console.log(value)) + + const setup = async (accounts: RequestAddressesReturnType) => { + userAddress = accounts[0] + try { + balance = await publicClient().getBalance({ + address: userAddress + }) + isConnected = true + + window.ethereum.on('accountsChanged', () => { + window.location.reload() + }) + } catch (error) { + console.error(error) + } + } + + const connectWallet = async () => { + if (window.ethereum) { + const accounts = await walletClient().requestAddresses() + + if (accounts.length > 0) { + await setup(accounts) + } + } + } + + const sendTransaction = async () => { + if (!userAddress || !window.ethereum) return + await walletClient().sendTransaction({ + account: userAddress, + to: '0x0000000000000000000000000000000000000000', + value: parseEther('0.000001') + }) + } + + const checkBalance = async () => { + if (!userAddress) return + + const ethBalance = await publicClient().getBalance({ + address: userAddress as `0x${string}` + }) + + console.log({ ethBalance }) + + const contract = getContract({ + address: PUBLIC_ERC20_CONTRACT_ADDRESS as `0x${string}`, + publicClient: publicClient(), + walletClient: walletClient(), + abi: erc20TokenAbi + }) + const result = await contract.read.balanceOf([userAddress] as [`0x${string}`]) + console.log(result) + } + + const createProposal = async () => { + const description = stringToHex('Testing proposal creation', { size: 32 }) + const blockWait = 100 + const targetVote = 500000 + + const { request } = await publicClient().simulateContract({ + address: PUBLIC_VOTE_CONTRACT_ADDRESS as `0x${string}`, + abi: voteContractAbi, + functionName: 'propose', + args: [description, BigInt(blockWait), targetVote], + account: userAddress + }) + const result = await walletClient().writeContract(request) + console.log(result) + } + + const getCurrentProposal = async () => { + const contract = getContract({ + address: PUBLIC_ERC20_CONTRACT_ADDRESS as `0x${string}`, + publicClient: publicClient(), + walletClient: walletClient(), + abi: voteContractAbi + }) + const result = await contract.read.getProposal([BigInt(1)]) + console.log(result) + } + + onMount(async () => { + if (window.ethereum) { + const accounts = await walletClient().getAddresses() + + if (accounts.length > 0) { + await setup(accounts) + } + } + }) </script> -<slot /> -\ No newline at end of file +<Nav on:click={connectWallet} /> +{#if isConnected} + <p class="text-xl text-green-600"> + Successfully connected with account: <strong>{userAddress}</strong> + </p> + <button class="btn btn-sm" on:click={sendTransaction}>Send transaction</button> + <button class="btn bt-sm" on:click={checkBalance}>Check balance</button> + <button class="btn" on:click={createProposal}>Create proposal</button> + <button class="btn" on:click={getCurrentProposal}>Get current proposal</button> + <ul> + <li>Current Network: {walletClient().chain.name}</li> + <li>Your current balance: {balance && formatEther(balance)} eth</li> + </ul> +{:else} + <button class="btn" on:click={connectWallet}>Connect with Wallet </button> +{/if} + +<slot /> diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte @@ -1,17 +1,15 @@ +<script lang="ts"> + import NetworkForm from '../components/NetworkForm.svelte' + +</script> <svelte:head> <title>EVM Token Vote</title> - <meta name="description" content="Vote on propositions with zero or more options using ERC20 tokens." /> + <meta + name="description" + content="Vote on propositions with zero or more options using ERC20 tokens." + /> </svelte:head> -<script lang="ts"> - import Nav from "../components/nav/nav.svelte"; -</script> - -<Nav /> - -<style lang="postcss"> - :global(html) { - background-color: theme(colors.gray.100); - } -</style> - -\ No newline at end of file +<div class="flex w-full min-h-screen"> + <NetworkForm /> +</div> +\ No newline at end of file diff --git a/src/routes/+page.ts b/src/routes/+page.ts @@ -1,3 +1 @@ -// since there's no dynamic data here, we can prerender -// it so that it gets served as a static asset in production -export const prerender = true; + diff --git a/src/shared/erc20-token-abi.ts b/src/shared/erc20-token-abi.ts @@ -0,0 +1,492 @@ +export const erc20TokenAbi = [ + { + constant: true, + inputs: [], + name: 'name', + outputs: [ + { + name: '', + type: 'string' + } + ], + payable: false, + stateMutability: 'view', + type: 'function' + }, + { + constant: false, + inputs: [ + { + name: '_spender', + type: 'address' + }, + { + name: '_value', + type: 'uint256' + } + ], + name: 'approve', + outputs: [ + { + name: '', + type: 'bool' + } + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function' + }, + { + constant: true, + inputs: [], + name: 'totalSupply', + outputs: [ + { + name: '', + type: 'uint256' + } + ], + payable: false, + stateMutability: 'view', + type: 'function' + }, + { + constant: false, + inputs: [ + { + name: '_from', + type: 'address' + }, + { + name: '_to', + type: 'address' + }, + { + name: '_value', + type: 'uint256' + } + ], + name: 'transferFrom', + outputs: [ + { + name: '', + type: 'bool' + } + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function' + }, + { + constant: true, + inputs: [], + name: 'decimals', + outputs: [ + { + name: '', + type: 'uint8' + } + ], + payable: false, + stateMutability: 'view', + type: 'function' + }, + { + constant: true, + inputs: [ + { + name: '_owner', + type: 'address' + } + ], + name: 'balanceOf', + outputs: [ + { + name: 'balance', + type: 'uint256' + } + ], + payable: false, + stateMutability: 'view', + type: 'function' + }, + { + constant: true, + inputs: [], + name: 'symbol', + outputs: [ + { + name: '', + type: 'string' + } + ], + payable: false, + stateMutability: 'view', + type: 'function' + }, + { + constant: false, + inputs: [ + { + name: '_to', + type: 'address' + }, + { + name: '_value', + type: 'uint256' + } + ], + name: 'transfer', + outputs: [ + { + name: '', + type: 'bool' + } + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function' + }, + { + constant: true, + inputs: [ + { + name: '_owner', + type: 'address' + }, + { + name: '_spender', + type: 'address' + } + ], + name: 'allowance', + outputs: [ + { + name: '', + type: 'uint256' + } + ], + payable: false, + stateMutability: 'view', + type: 'function' + }, + { + payable: true, + stateMutability: 'payable', + type: 'fallback' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: 'owner', + type: 'address' + }, + { + indexed: true, + name: 'spender', + type: 'address' + }, + { + indexed: false, + name: 'value', + type: 'uint256' + } + ], + name: 'Approval', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: 'from', + type: 'address' + }, + { + indexed: true, + name: 'to', + type: 'address' + }, + { + indexed: false, + name: 'value', + type: 'uint256' + } + ], + name: 'Transfer', + type: 'event' + } +] as const + +export const gitableAbi = [ + { + inputs: [ + { internalType: 'string', name: '_name', type: 'string' }, + { internalType: 'string', name: '_symbol', type: 'string' }, + { internalType: 'uint8', name: '_decimals', type: 'uint8' }, + { internalType: 'uint256', name: '_expireTimestamp', type: 'uint256' } + ], + stateMutability: 'nonpayable', + type: 'constructor' + }, + { + 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: [{ indexed: false, internalType: 'uint256', name: '_value', type: 'uint256' }], + name: 'Burn', + type: 'event' + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: 'uint256', name: '_timestamp', type: 'uint256' }], + name: 'Expired', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: '_minter', type: 'address' }, + { indexed: true, internalType: 'address', name: '_beneficiary', type: 'address' }, + { indexed: false, internalType: 'uint256', name: '_value', type: 'uint256' } + ], + name: 'Mint', + 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' + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: '_from', type: 'address' }, + { indexed: true, internalType: 'address', name: '_to', type: 'address' }, + { indexed: true, internalType: 'address', name: '_spender', type: 'address' }, + { indexed: false, internalType: 'uint256', name: '_value', type: 'uint256' } + ], + name: 'TransferFrom', + type: 'event' + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: 'address', name: '_writer', type: 'address' }], + name: 'WriterAdded', + type: 'event' + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: 'address', name: '_writer', type: 'address' }], + name: 'WriterRemoved', + type: 'event' + }, + { + inputs: [{ internalType: 'address', name: '_minter', type: 'address' }], + name: 'addWriter', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: '', type: 'address' }, + { internalType: 'address', name: '', type: 'address' } + ], + name: 'allowance', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'applyExpiry', + outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], + stateMutability: 'nonpayable', + 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: '', type: 'address' }], + name: 'balanceOf', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'uint256', name: '_value', type: 'uint256' }], + name: 'burn', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: '_from', type: 'address' }, + { internalType: 'uint256', name: '_value', type: 'uint256' }, + { internalType: 'bytes', name: '_data', type: 'bytes' } + ], + name: 'burn', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [], + name: 'burn', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [], + name: 'decimals', + outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: '_minter', type: 'address' }], + name: 'deleteWriter', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [], + name: 'expires', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: '_minter', type: 'address' }], + name: 'isWriter', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_value', type: 'uint256' }, + { internalType: 'bytes', name: '_data', type: 'bytes' } + ], + name: 'mint', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_value', type: 'uint256' } + ], + name: 'mintTo', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [], + name: 'name', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'owner', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'bytes4', name: '_sum', type: 'bytes4' }], + name: 'supportsInterface', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'pure', + type: 'function' + }, + { + inputs: [], + name: 'symbol', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'totalBurned', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'totalMinted', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + 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: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function' + } +] as const +\ No newline at end of file diff --git a/src/shared/localstorage-keys.ts b/src/shared/localstorage-keys.ts @@ -0,0 +1,4 @@ +export const LOCAL_STORAGE_KEYS = { + CHAIN_CONFIG: 'EVM_TOKEN_VOTE_CHAIN_CONFIG', + VOTING_CONFIG: 'EVM_TOKEN_VOTE_VOTING_CONFIG' +} +\ No newline at end of file diff --git a/src/shared/token-vote-abi.ts b/src/shared/token-vote-abi.ts @@ -0,0 +1,523 @@ +export const voteContractAbi = [ + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "bool", + "name": "_protectSupply", + "type": "bool" + }, + { + "internalType": "address", + "name": "_voterRegistry", + "type": "address" + }, + { + "internalType": "address", + "name": "_proposerRegistry", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "_blockDeadline", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "voteTargetPpm", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "_proposalIdx", + "type": "uint256" + } + ], + "name": "ProposalAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "_proposalIdx", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "_cancelled", + "type": "bool" + }, + { + "indexed": true, + "internalType": "bool", + "name": "_insufficient", + "type": "bool" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_totalVote", + "type": "uint256" + } + ], + "name": "ProposalCompleted", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "finalize", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentProposal", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "description", + "type": "bytes32" + }, + { + "internalType": "bytes32[]", + "name": "options", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "optionVotes", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "cancelVotes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockDeadline", + "type": "uint256" + }, + { + "internalType": "uint24", + "name": "targetVotePpm", + "type": "uint24" + }, + { + "internalType": "address", + "name": "proposer", + "type": "address" + }, + { + "internalType": "uint8", + "name": "state", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "scanCursor", + "type": "uint8" + } + ], + "internalType": "struct ERC20Vote.Proposal", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_proposalIdx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_optionIdx", + "type": "uint256" + } + ], + "name": "getOption", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_proposalIdx", + "type": "uint256" + } + ], + "name": "getProposal", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "description", + "type": "bytes32" + }, + { + "internalType": "bytes32[]", + "name": "options", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "optionVotes", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "cancelVotes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockDeadline", + "type": "uint256" + }, + { + "internalType": "uint24", + "name": "targetVotePpm", + "type": "uint24" + }, + { + "internalType": "address", + "name": "proposer", + "type": "address" + }, + { + "internalType": "uint8", + "name": "state", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "scanCursor", + "type": "uint8" + } + ], + "internalType": "struct ERC20Vote.Proposal", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_proposalIdx", + "type": "uint256" + } + ], + "name": "optionCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_description", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_blockWait", + "type": "uint256" + }, + { + "internalType": "uint24", + "name": "_targetVotePpm", + "type": "uint24" + } + ], + "name": "propose", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_description", + "type": "bytes32" + }, + { + "internalType": "bytes32[]", + "name": "_options", + "type": "bytes32[]" + }, + { + "internalType": "uint256", + "name": "_blockWait", + "type": "uint256" + }, + { + "internalType": "uint24", + "name": "_targetVotePpm", + "type": "uint24" + } + ], + "name": "proposeMulti", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_proposalIndex", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "_count", + "type": "uint8" + } + ], + "name": "scan", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "token", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "vote", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "voteCancel", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_proposalIdx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_optionIdx", + "type": "uint256" + } + ], + "name": "voteCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_optionIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "voteOption", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdraw", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] as const diff --git a/src/shared/types.ts b/src/shared/types.ts @@ -0,0 +1,4 @@ +export interface VotingConfig { + contractAddress: string + erc20ContractAddress: string +} +\ No newline at end of file diff --git a/src/store.ts b/src/store.ts @@ -0,0 +1,77 @@ +import { writable } from 'svelte/store' +import { browser } from '$app/environment' +import type { Chain } from '@wagmi/chains' +import { + PUBLIC_RPC_URL, + PUBLIC_CHAIN_ID, + PUBLIC_CHAIN_NAME, + PUBLIC_CURRENCY_NAME, + PUBLIC_CURRENCY_SYMBOL, + PUBLIC_DECIMALS, + PUBLIC_VOTE_CONTRACT_ADDRESS, + PUBLIC_ERC20_CONTRACT_ADDRESS +} from '$env/static/public' +import type { VotingConfig } from './shared/types' +import { LOCAL_STORAGE_KEYS } from './shared/localstorage-keys' + +const createChain = () => { + const initialChain: Chain = { + id: parseInt(PUBLIC_CHAIN_ID, 10), + name: PUBLIC_CHAIN_NAME, + rpcUrls: { + default: { + http: [PUBLIC_RPC_URL], + webSocket: undefined + }, + public: { + http: [], + webSocket: undefined + } + }, + nativeCurrency: { + name: PUBLIC_CURRENCY_NAME, + symbol: PUBLIC_CURRENCY_SYMBOL, + decimals: parseInt(PUBLIC_DECIMALS, 10) + }, + network: PUBLIC_CHAIN_NAME + } + + let localValue; + if (browser) { + localValue = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.CHAIN_CONFIG) || '{}') + } + console.log({localValue}) + + const { subscribe, update } = writable<Chain>(localValue || initialChain) + + return { + subscribe, + updateChain: (newChain: Chain) => + update((current) => { + const chain = { ...current, ...newChain } + // update localstorage + if (browser) { + localStorage.setItem(LOCAL_STORAGE_KEYS.CHAIN_CONFIG, JSON.stringify(chain)) + } + return chain + }) + } +} + +export const createVotingConfig = () => { + const initialVotingConfig: VotingConfig = { + contractAddress: PUBLIC_VOTE_CONTRACT_ADDRESS, + erc20ContractAddress: PUBLIC_ERC20_CONTRACT_ADDRESS + } + + const { subscribe, update } = writable<VotingConfig>(initialVotingConfig) + + return { + subscribe, + updateVotingConfig: (newConfig: VotingConfig) => + update((current) => ({ ...current, ...newConfig })) + } +} + +export const configuredChain = createChain() +export const votingConfig = createVotingConfig() diff --git a/tailwind.config.js b/tailwind.config.js @@ -8,6 +8,23 @@ export default { } }, }, - plugins: [], + daisyui: { + themes: [ + { + mytheme: { + "primary": "#00d1b2", + "secondary": "#a989f9", + "accent": "#ffcce8", + "neutral": "#322541", + "base-100": "#ffffff", + "info": "#abceed", + "success": "#65e2b2", + "warning": "#eeb21b", + "error": "#f71d62", + }, + }, + ], + }, + plugins: [require("daisyui")], }