diff --git a/client/src/App.jsx b/client/src/App.jsx index 4170ccf..8d21b7d 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -144,6 +144,8 @@ function App() { /* --------------------------- BEGIN EVENT HANDLERS ------------------------------- */ /* -------------------------------------------------------------------------------- */ + // TODO: Unsubscribe from events when effect is to be rerun + DAOContract.events.PostAdded({ fromBlock: 'latest' }).on('data', (event) => { console.log('event: post added', event); fetchPost(event.returnValues.postIndex); diff --git a/ethereum/.env.example b/ethereum/.env.example index 49eaa36..7fdc978 100644 --- a/ethereum/.env.example +++ b/ethereum/.env.example @@ -1,3 +1,5 @@ +API_URL="https://demo.dgov.io/api" +CA_PATH= SEPOLIA_PRIVATE_KEY= ETHERSCAN_API_KEY= WORK1_PRICE="0.001" diff --git a/ethereum/package-lock.json b/ethereum/package-lock.json index 3b2c6de..6be5d75 100644 --- a/ethereum/package-lock.json +++ b/ethereum/package-lock.json @@ -8,16 +8,15 @@ "name": "dgf-prototype", "version": "1.0.0", "license": "ISC", - "dependencies": { - "@openzeppelin/contracts": "^5.0.2", - "dotenv": "^16.4.5" - }, "devDependencies": { + "@metamask/eth-sig-util": "^7.0.1", "@nomicfoundation/hardhat-chai-matchers": "^2.0.6", "@nomicfoundation/hardhat-toolbox": "^4.0.0", "@nomicfoundation/hardhat-verify": "^2.0.5", + "@openzeppelin/contracts": "^5.0.2", + "axios": "^1.6.8", "chai": "^4.4.1", - "contract-config": "file://../contract-config", + "dotenv": "^16.4.5", "eslint": "^8.57.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-chai-friendly": "^0.7.4", @@ -27,13 +26,14 @@ "eslint-plugin-react-hooks": "^4.6.0", "hardhat": "^2.20.1", "mocha": "^10.3.0", + "object-hash": "^3.0.0", "prettier": "^3.2.5", "prettier-plugin-solidity": "^1.3.1" } }, "../contract-config": { "version": "1.0.0", - "dev": true, + "extraneous": true, "license": "ISC" }, "node_modules/@aashutoshrathi/word-wrap": { @@ -155,12 +155,21 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@ethereumjs/common": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-3.2.0.tgz", + "integrity": "sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==", + "dev": true, + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "crc-32": "^1.2.0" + } + }, "node_modules/@ethereumjs/rlp": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", "dev": true, - "peer": true, "bin": { "rlp": "bin/rlp" }, @@ -168,12 +177,89 @@ "node": ">=14" } }, + "node_modules/@ethereumjs/tx": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-4.2.0.tgz", + "integrity": "sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==", + "dev": true, + "dependencies": { + "@ethereumjs/common": "^3.2.0", + "@ethereumjs/rlp": "^4.0.1", + "@ethereumjs/util": "^8.1.0", + "ethereum-cryptography": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/tx/node_modules/@noble/curves": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", + "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.3.3" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/tx/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "dev": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/tx/node_modules/@scure/bip32": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", + "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", + "dev": true, + "dependencies": { + "@noble/curves": "~1.3.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/tx/node_modules/@scure/bip39": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", + "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", + "dev": true, + "dependencies": { + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/tx/node_modules/ethereum-cryptography": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", + "integrity": "sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==", + "dev": true, + "dependencies": { + "@noble/curves": "1.3.0", + "@noble/hashes": "1.3.3", + "@scure/bip32": "1.3.3", + "@scure/bip39": "1.2.2" + } + }, "node_modules/@ethereumjs/util": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", "dev": true, - "peer": true, "dependencies": { "@ethereumjs/rlp": "^4.0.1", "ethereum-cryptography": "^2.0.0", @@ -188,7 +274,6 @@ "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", "dev": true, - "peer": true, "dependencies": { "@noble/hashes": "1.3.3" }, @@ -201,7 +286,6 @@ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", "dev": true, - "peer": true, "engines": { "node": ">= 16" }, @@ -214,7 +298,6 @@ "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", "dev": true, - "peer": true, "dependencies": { "@noble/curves": "~1.3.0", "@noble/hashes": "~1.3.2", @@ -229,7 +312,6 @@ "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", "dev": true, - "peer": true, "dependencies": { "@noble/hashes": "~1.3.2", "@scure/base": "~1.1.4" @@ -243,7 +325,6 @@ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", "integrity": "sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==", "dev": true, - "peer": true, "dependencies": { "@noble/curves": "1.3.0", "@noble/hashes": "1.3.3", @@ -1063,20 +1144,169 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@metamask/eth-sig-util": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", - "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "node_modules/@metamask/abi-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@metamask/abi-utils/-/abi-utils-2.0.2.tgz", + "integrity": "sha512-B/A1dY/w4F/t6cDHUscklO6ovb/ztFsrsTXFd8QlqSByk/vyy+QbPE3VVpmmyI/7RX+PA1AJcvBdzCIz+r9dVQ==", "dev": true, "dependencies": { - "ethereumjs-abi": "^0.6.8", - "ethereumjs-util": "^6.2.1", - "ethjs-util": "^0.1.6", + "@metamask/utils": "^8.0.0", + "superstruct": "^1.0.3" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/eth-sig-util": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-7.0.1.tgz", + "integrity": "sha512-59GSrMyFH2fPfu7nKeIQdZ150zxXNNhAQIUaFRUW+MGtVA4w/ONbiQobcRBLi+jQProfIyss51G8pfLPcQ0ylg==", + "dev": true, + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "@metamask/abi-utils": "^2.0.2", + "@metamask/utils": "^8.1.0", + "ethereum-cryptography": "^2.1.2", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1" }, "engines": { - "node": ">=12.0.0" + "node": "^16.20 || ^18.16 || >=20" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/@noble/curves": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", + "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.3.3" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "dev": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/@scure/bip32": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", + "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", + "dev": true, + "dependencies": { + "@noble/curves": "~1.3.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/@scure/bip39": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", + "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", + "dev": true, + "dependencies": { + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/ethereum-cryptography": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", + "integrity": "sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==", + "dev": true, + "dependencies": { + "@noble/curves": "1.3.0", + "@noble/hashes": "1.3.3", + "@scure/bip32": "1.3.3", + "@scure/bip39": "1.2.2" + } + }, + "node_modules/@metamask/utils": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.4.0.tgz", + "integrity": "sha512-dbIc3C7alOe0agCuBHM1h71UaEaEqOk2W8rAtEn8QGz4haH2Qq7MoK6i7v2guzvkJVVh79c+QCzIqphC3KvrJg==", + "dev": true, + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "superstruct": "^1.0.3", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/utils/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@metamask/utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@metamask/utils/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@metamask/utils/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/@noble/curves": { @@ -1903,7 +2133,8 @@ "node_modules/@openzeppelin/contracts": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.0.2.tgz", - "integrity": "sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==" + "integrity": "sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==", + "dev": true }, "node_modules/@scure/base": { "version": "1.1.5", @@ -2769,8 +3000,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "peer": true + "dev": true }, "node_modules/at-least-node": { "version": "1.0.0", @@ -2807,13 +3037,12 @@ } }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dev": true, - "peer": true, "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -3363,7 +3592,6 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, - "peer": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -3489,10 +3717,6 @@ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, - "node_modules/contract-config": { - "resolved": "../contract-config", - "link": true - }, "node_modules/cookie": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", @@ -3509,6 +3733,18 @@ "dev": true, "peer": true }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -3691,7 +3927,6 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, - "peer": true, "engines": { "node": ">=0.4.0" } @@ -3765,6 +4000,7 @@ "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, "engines": { "node": ">=12" }, @@ -5162,9 +5398,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "dev": true, "funding": [ { @@ -5195,7 +5431,6 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, - "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -5622,6 +5857,22 @@ "hardhat": "^2.0.2" } }, + "node_modules/hardhat/node_modules/@metamask/eth-sig-util": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", + "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "dev": true, + "dependencies": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^6.2.1", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -6753,8 +7004,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", - "dev": true, - "peer": true + "dev": true }, "node_modules/micromatch": { "version": "4.0.5", @@ -6775,7 +7025,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "peer": true, "engines": { "node": ">= 0.6" } @@ -6785,7 +7034,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -7179,6 +7427,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -7494,6 +7751,15 @@ "node": ">=6" } }, + "node_modules/pony-cause": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.10.tgz", + "integrity": "sha512-3IKLNXclQgkU++2fSi93sQ6BznFuxSLB11HdvZQ6JW/spahf/P1pAHBQEahr20rs0htZW0UDkM1HmA+nZkXKsw==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -7610,8 +7876,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true, - "peer": true + "dev": true }, "node_modules/punycode": { "version": "2.3.1", @@ -8696,6 +8961,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/superstruct": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-1.0.4.tgz", + "integrity": "sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", diff --git a/ethereum/package.json b/ethereum/package.json index 9b0d79b..c6700f6 100644 --- a/ethereum/package.json +++ b/ethereum/package.json @@ -16,10 +16,14 @@ "author": "", "license": "ISC", "devDependencies": { + "@metamask/eth-sig-util": "^7.0.1", "@nomicfoundation/hardhat-chai-matchers": "^2.0.6", "@nomicfoundation/hardhat-toolbox": "^4.0.0", "@nomicfoundation/hardhat-verify": "^2.0.5", + "@openzeppelin/contracts": "^5.0.2", + "axios": "^1.6.8", "chai": "^4.4.1", + "dotenv": "^16.4.5", "eslint": "^8.57.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-chai-friendly": "^0.7.4", @@ -29,11 +33,8 @@ "eslint-plugin-react-hooks": "^4.6.0", "hardhat": "^2.20.1", "mocha": "^10.3.0", + "object-hash": "^3.0.0", "prettier": "^3.2.5", "prettier-plugin-solidity": "^1.3.1" - }, - "dependencies": { - "@openzeppelin/contracts": "^5.0.2", - "dotenv": "^16.4.5" } } diff --git a/ethereum/scripts/automatic-staking.js b/ethereum/scripts/automatic-staking.js index 9c5449a..19e0242 100644 --- a/ethereum/scripts/automatic-staking.js +++ b/ethereum/scripts/automatic-staking.js @@ -1,5 +1,6 @@ const { ethers } = require('hardhat'); const { getContractAddressByNetworkName } = require('./contract-config'); +const readFromApi = require('./util/read-from-api'); const network = process.env.HARDHAT_NETWORK; @@ -9,14 +10,37 @@ let onboarding; let account; let validationPools; let reputation; +let posts; const fetchReputation = async () => { reputation = await dao.balanceOf(account); console.log(`reputation: ${reputation}`); }; +const fetchPost = async (postIndex) => { + const { + id, sender, author, contentId, + } = await dao.posts(postIndex); + const { content } = await readFromApi(contentId); + const post = { + id, + sender, + author, + contentId, + content, + }; + posts[postIndex] = post; + return post; +}; + const fetchValidationPool = async (poolIndex) => { - const pool = await dao.validationPools(poolIndex); + const { + id, postIndex, sender, stakeCount, fee, duration, endTime, resolved, outcome, + } = await dao.validationPools(poolIndex); + const pool = { + id, postIndex, sender, stakeCount, fee, duration, endTime, resolved, outcome, + }; + pool.post = await fetchPost(pool.postIndex); validationPools[poolIndex] = pool; return pool; }; @@ -43,6 +67,7 @@ const initialize = async () => { [account] = await ethers.getSigners(); const address = await account.getAddress(); console.log(`account: ${address}`); + posts = []; await fetchReputation(); await fetchValidationPools(); }; @@ -53,10 +78,21 @@ const poolIsActive = (pool) => { return true; }; -const poolIsValid = (pool) => [ - getContractAddressByNetworkName(network, 'Work1'), - getContractAddressByNetworkName(network, 'Onboarding'), -].includes(pool.sender); +const poolIsValid = async (pool) => { + switch (pool.sender) { + case getContractAddressByNetworkName(network, 'Work1'): { + // TODO: Read content of post for this pool and verify that it conforms to protocol + console.log(`post ${pool.post.id} content: ${pool.post.content}`); + if (pool.post.content) return true; + return true; + } + case getContractAddressByNetworkName(network, 'Onboarding'): + // TODO: Read content of post for this pool and verify that it conforms to protocol + return true; + default: + return false; + } +}; const getPoolStatus = (pool) => { if (poolIsActive(pool)) return 'Active'; @@ -73,8 +109,9 @@ const stake = async (pool, amount, inFavor) => { const stakeEach = async (pools, amountPerPool) => { const promises = []; - pools.forEach((pool) => { - promises.push(stake(pool, amountPerPool, poolIsValid(pool))); + pools.forEach(async (pool) => { + const inFavor = await poolIsValid(pool); + promises.push(stake(pool, amountPerPool, inFavor)); }); await Promise.all(promises); }; @@ -83,7 +120,7 @@ async function main() { await initialize(); validationPools.forEach((pool) => { - console.log(`pool ${pool.id.toString()}, status: ${getPoolStatus(pool)}`); + console.log(`pool ${pool.id.toString()}, status: ${getPoolStatus(pool)}, post content: ${pool.post?.content}`); }); // Stake half of available reputation on any active pools @@ -100,7 +137,7 @@ async function main() { await fetchReputation(); if (!reputation) return; const amountToStake = reputation / BigInt(2); - if (poolIsValid(pool)) { + if (await poolIsValid(pool)) { // Stake half of available reputation on this validation pool await stake(pool, amountToStake, true); } else { diff --git a/ethereum/scripts/util/read-from-api.js b/ethereum/scripts/util/read-from-api.js new file mode 100644 index 0000000..6eaae70 --- /dev/null +++ b/ethereum/scripts/util/read-from-api.js @@ -0,0 +1,35 @@ +const axios = require('axios'); +const { readFileSync } = require('fs'); +const https = require('https'); +const objectHash = require('object-hash'); +const { recoverPersonalSignature } = require('@metamask/eth-sig-util'); + +require('dotenv').config(); + +const apiUrl = process.env.API_URL; +const caPath = process.env.CA_PATH; + +const readFromApi = async (hash) => { + const options = {}; + if (caPath) { + options.httpsAgent = new https.Agent({ + ca: readFileSync(caPath), + }); + } + const { data: { author, content, signature } } = await axios.get(`${apiUrl}/read/${hash}`, options); + + // Verify hash + const derivedHash = objectHash({ author, content, signature }); + if (derivedHash !== hash) { + throw new Error('hash mismatch'); + } + + // Verify signature + const account = recoverPersonalSignature({ data: content, signature }); + if (account !== author) { + throw new Error('author does not match signature'); + } + return { author, content, signature }; +}; + +module.exports = readFromApi;