From 956aa9728e114b3de044ad03e665cf14ecfdf52e Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Wed, 20 Mar 2024 16:30:27 -0500 Subject: [PATCH] require signatures for posts to API --- backend/index.js | 22 +- backend/package-lock.json | 394 +++++++++++++++++++------ backend/package.json | 2 +- client/package-lock.json | 193 ++---------- client/package.json | 1 + client/src/App.jsx | 10 +- client/src/components/AddPostModal.jsx | 16 +- client/src/utils/contract-config.js | 6 +- ethereum/scripts/automatic-staking.js | 25 +- ethereum/scripts/contract-config.js | 10 +- 10 files changed, 400 insertions(+), 279 deletions(-) diff --git a/backend/index.js b/backend/index.js index de947ce..dc9afc7 100644 --- a/backend/index.js +++ b/backend/index.js @@ -1,5 +1,8 @@ const express = require('express'); const { Level } = require('level'); +const { recoverPersonalSignature } = require('@metamask/eth-sig-util'); +// const { ecrecover, fromRpcSig, pubToAddress } = require('@ethereumjs/util'); +// const { Keccak } = require('sha3'); const { createHash, } = require('node:crypto'); @@ -12,12 +15,27 @@ const dataDir = process.env.DATA_DIR || 'data'; const db = new Level(`${dataDir}/forum`, { valueEncoding: 'json' }); +// const keccak = Keccak(256); + app.use(express.json()); app.post('/write', async (req, res) => { - const { body: data } = req; - // TODO: Require author signature + const { body: { author, content, signature } } = req; + // Check author signature + try { + const account = recoverPersonalSignature({ data: content, signature }); + if (account !== author) { + console.log('error: author does not match signature'); + res.status(403).end(); + return; + } + } catch (e) { + console.log('error: failed to recover signature:', e.message); + res.status(400).end(); + return; + } // Compute content hash + const data = { author, content, signature }; const hash = createHash('sha256').update(JSON.stringify(data)).digest('base64url'); console.log('write', hash); // Store content diff --git a/backend/package-lock.json b/backend/package-lock.json index c175f04..5974a01 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -9,9 +9,9 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@metamask/eth-sig-util": "^7.0.1", "axios": "^1.6.7", "dotenv": "^16.4.5", - "ethers": "^6.11.1", "express": "^4.18.2", "level": "^8.0.1" }, @@ -33,11 +33,6 @@ "node": ">=0.10.0" } }, - "node_modules/@adraffy/ens-normalize": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", - "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" - }, "node_modules/@babel/runtime": { "version": "7.23.9", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", @@ -106,6 +101,77 @@ "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==", + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "crc-32": "^1.2.0" + } + }, + "node_modules/@ethereumjs/common/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==", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/common/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==", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "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==", + "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/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/tx/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==", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -139,6 +205,113 @@ "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, + "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==", + "dependencies": { + "@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==", + "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": "^16.20 || ^18.16 || >=20" + } + }, + "node_modules/@metamask/eth-sig-util/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==", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@metamask/eth-sig-util/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==", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@metamask/utils": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.4.0.tgz", + "integrity": "sha512-dbIc3C7alOe0agCuBHM1h71UaEaEqOk2W8rAtEn8QGz4haH2Qq7MoK6i7v2guzvkJVVh79c+QCzIqphC3KvrJg==", + "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/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "dependencies": { + "@noble/hashes": "1.3.3" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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==", + "engines": { + "node": ">= 16" + }, + "funding": { + "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", @@ -174,12 +347,58 @@ "node": ">= 8" } }, + "node_modules/@scure/base": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.6.tgz", + "integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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==", + "dependencies": { + "@noble/curves": "~1.3.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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==", + "dependencies": { + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -236,11 +455,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.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", @@ -761,6 +975,17 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "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==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -785,7 +1010,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -1465,83 +1689,15 @@ "node": ">= 0.6" } }, - "node_modules/ethers": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.11.1.tgz", - "integrity": "sha512-mxTAE6wqJQAbp5QAe/+o+rXOID7Nw91OZXvgpjDa1r4fAbq2Nu314oEZSbjoRLacuCzs7kUC3clEvkCQowffGg==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/ethers-io/" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], + "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==", "dependencies": { - "@adraffy/ens-normalize": "1.10.1", - "@noble/curves": "1.2.0", - "@noble/hashes": "1.3.2", - "@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/@noble/curves": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", - "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", - "dependencies": { - "@noble/hashes": "1.3.2" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ethers/node_modules/@noble/hashes": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "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/ethers/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 - } + "@noble/curves": "1.3.0", + "@noble/hashes": "1.3.3", + "@scure/bip32": "1.3.3", + "@scure/bip39": "1.2.2" } }, "node_modules/express": { @@ -2638,6 +2794,17 @@ "loose-envify": "cli.js" } }, + "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==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -2659,6 +2826,11 @@ "node": ">= 0.6" } }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==" + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -2721,8 +2893,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/napi-macros": { "version": "2.2.2", @@ -2996,6 +3167,14 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "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==", + "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", @@ -3556,6 +3735,14 @@ "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==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3606,6 +3793,16 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3755,6 +3952,18 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3860,6 +4069,11 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/backend/package.json b/backend/package.json index 4e8fde3..8e7dc6d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -9,9 +9,9 @@ "author": "", "license": "ISC", "dependencies": { + "@metamask/eth-sig-util": "^7.0.1", "axios": "^1.6.7", "dotenv": "^16.4.5", - "ethers": "^6.11.1", "express": "^4.18.2", "level": "^8.0.1" }, diff --git a/client/package-lock.json b/client/package-lock.json index 4927fc6..8a8fea4 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -16,6 +16,7 @@ "axios": "^1.6.8", "bootstrap": "^5.3.3", "bootswatch": "^5.3.3", + "buffer": "^6.0.3", "helia": "^4.1.0", "ipfs-core": "^0.18.1", "prop-types": "^15.8.1", @@ -5642,29 +5643,6 @@ "uint8arrays": "^5.0.2" } }, - "node_modules/@multiformats/dns/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/@multiformats/mafmt": { "version": "12.1.6", "resolved": "https://registry.npmjs.org/@multiformats/mafmt/-/mafmt-12.1.6.tgz", @@ -8281,29 +8259,6 @@ "node": ">=12" } }, - "node_modules/abstract-level/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/abstract-leveldown": { "version": "0.12.4", "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-0.12.4.tgz", @@ -8861,6 +8816,29 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/blob-to-it": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/blob-to-it/-/blob-to-it-2.0.5.tgz", @@ -9281,9 +9259,9 @@ } }, "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "funding": [ { "type": "github", @@ -9300,7 +9278,7 @@ ], "dependencies": { "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "ieee754": "^1.2.1" } }, "node_modules/buffer-es6": { @@ -14906,29 +14884,6 @@ "npm": ">=7.0.0" } }, - "node_modules/ipfs-core/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/ipfs-core/node_modules/cborg": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/cborg/-/cborg-1.10.2.tgz", @@ -16700,29 +16655,6 @@ "resolved": "https://registry.npmjs.org/browser-readablestream-to-it/-/browser-readablestream-to-it-1.0.3.tgz", "integrity": "sha512-+12sHB+Br8HIh6VAMVEG5r3UXCyESIgDW7kzk3BjIXa43DVqVwL7GC5TW3jeh+72dtcH99pPVpw0X8i0jt+/kw==" }, - "node_modules/ipfs-utils/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/ipfs-utils/node_modules/it-all": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/it-all/-/it-all-1.0.6.tgz", @@ -17708,29 +17640,6 @@ "readable-stream": "^3.6.0" } }, - "node_modules/it-to-stream/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/it-to-stream/node_modules/get-iterator": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-iterator/-/get-iterator-1.0.2.tgz", @@ -18712,29 +18621,6 @@ "node": ">=12" } }, - "node_modules/level-transcoder/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/levelup": { "version": "0.18.6", "resolved": "https://registry.npmjs.org/levelup/-/levelup-0.18.6.tgz", @@ -21492,29 +21378,6 @@ "readable-stream": "^3.4.0" } }, - "node_modules/rabin-wasm/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/race-event": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/race-event/-/race-event-1.2.0.tgz", diff --git a/client/package.json b/client/package.json index 17cc44b..f634be0 100644 --- a/client/package.json +++ b/client/package.json @@ -18,6 +18,7 @@ "axios": "^1.6.8", "bootstrap": "^5.3.3", "bootswatch": "^5.3.3", + "buffer": "^6.0.3", "helia": "^4.1.0", "ipfs-core": "^0.18.1", "prop-types": "^15.8.1", diff --git a/client/src/App.jsx b/client/src/App.jsx index 957beee..78fd946 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -16,7 +16,7 @@ import Stack from 'react-bootstrap/Stack'; import './App.css'; import useList from './utils/List'; -import { getContractByChainId as getContractAdressByChainId, getContractNameByAddress } from './utils/contract-config'; +import { getContractAddressByChainId, getContractNameByAddress } from './utils/contract-config'; import Web3Context from './contexts/Web3Context'; import DAOArtifact from './assets/DAO.json'; import Work1Artifact from './assets/Work1.json'; @@ -58,9 +58,9 @@ function App() { // In this effect, we initialize everything and add contract event listeners. useEffect(() => { if (!provider || !chainId || !account || balance === undefined) return; - const DAOAddress = getContractAdressByChainId(chainId, 'DAO'); - const Work1Address = getContractAdressByChainId(chainId, 'Work1'); - const OnboardingAddress = getContractAdressByChainId(chainId, 'Onboarding'); + const DAOAddress = getContractAddressByChainId(chainId, 'DAO'); + const Work1Address = getContractAddressByChainId(chainId, 'Work1'); + const OnboardingAddress = getContractAddressByChainId(chainId, 'Onboarding'); const web3 = new Web3(provider); const DAOContract = new web3.eth.Contract(DAOArtifact.abi, DAOAddress); const Work1Contract = new web3.eth.Contract(Work1Artifact.abi, Work1Address); @@ -205,7 +205,7 @@ function App() { params: { type: 'ERC20', options: { - address: getContractAdressByChainId(chainId, 'DAO'), + address: getContractAddressByChainId(chainId, 'DAO'), }, }, }); diff --git a/client/src/components/AddPostModal.jsx b/client/src/components/AddPostModal.jsx index 559f6a7..2a3a5f9 100644 --- a/client/src/components/AddPostModal.jsx +++ b/client/src/components/AddPostModal.jsx @@ -4,13 +4,16 @@ import Form from 'react-bootstrap/Form'; import Modal from 'react-bootstrap/Modal'; import axios from 'axios'; import PropTypes from 'prop-types'; +import { Buffer } from 'buffer/'; // note: the trailing slash is important! import Web3Context from '../contexts/Web3Context'; function AddPostModal({ show, setShow, title, postToBlockchain, onSubmit, }) { - const { DAO, account } = useContext(Web3Context); + const { + provider, DAO, account, + } = useContext(Web3Context); const [content, setContent] = useState(''); const handleClose = () => setShow(false); @@ -18,7 +21,14 @@ function AddPostModal({ const handleSubmit = useCallback(async () => { // Upload content to API // TODO: include metamask signature - const data = { author: account, content }; + const msg = `0x${Buffer.from(content, 'utf8').toString('hex')}`; + const signature = await provider.request({ + method: 'personal_sign', + params: [msg, account], + }); + const data = { + author: account, content, signature, + }; const res = await axios.post('/api/write', data); const hash = res.data; setShow(false); @@ -33,7 +43,7 @@ function AddPostModal({ if (onSubmit) { onSubmit(hash, data); } - }, [DAO, account, content, setShow, postToBlockchain, onSubmit]); + }, [provider, DAO, account, content, setShow, postToBlockchain, onSubmit]); return ( diff --git a/client/src/utils/contract-config.js b/client/src/utils/contract-config.js index 7d3925b..a0eca81 100644 --- a/client/src/utils/contract-config.js +++ b/client/src/utils/contract-config.js @@ -5,16 +5,16 @@ const networks = { sepolia: '0xaa36a7', }; -export const getContractByNetworkName = (networkName, contractName) => { +export const getContractAddressByNetworkName = (networkName, contractName) => { const address = contractAddresses[networkName][contractName]; if (!address) throw new Error(`Contract ${contractName} not recognized`); return address; }; -export const getContractByChainId = (chainId, contractName) => { +export const getContractAddressByChainId = (chainId, contractName) => { const network = Object.entries(networks).find(([, id]) => id === chainId)[0]; if (!network) throw new Error(`Chain ID ${chainId} not recognized`); - return getContractByNetworkName(network, contractName); + return getContractAddressByNetworkName(network, contractName); }; export const getContractNameByAddress = (chainId, address) => { diff --git a/ethereum/scripts/automatic-staking.js b/ethereum/scripts/automatic-staking.js index 0130ada..9c5449a 100644 --- a/ethereum/scripts/automatic-staking.js +++ b/ethereum/scripts/automatic-staking.js @@ -1,9 +1,11 @@ const { ethers } = require('hardhat'); -const { getContractByNetworkName } = require('./contract-config'); +const { getContractAddressByNetworkName } = require('./contract-config'); const network = process.env.HARDHAT_NETWORK; let dao; +let work1; +let onboarding; let account; let validationPools; let reputation; @@ -31,8 +33,13 @@ const fetchValidationPools = async () => { }; const initialize = async () => { - const DAOAddress = getContractByNetworkName(network, 'DAO'); - dao = await ethers.getContractAt('DAO', DAOAddress); + const getContract = (name) => ethers.getContractAt( + name, + getContractAddressByNetworkName(network, name), + ); + dao = await getContract('DAO'); + work1 = await getContract('Work1'); + onboarding = await getContract('Onboarding'); [account] = await ethers.getSigners(); const address = await account.getAddress(); console.log(`account: ${address}`); @@ -47,8 +54,8 @@ const poolIsActive = (pool) => { }; const poolIsValid = (pool) => [ - getContractByNetworkName(network, 'Work1'), - getContractByNetworkName(network, 'Onboarding'), + getContractAddressByNetworkName(network, 'Work1'), + getContractAddressByNetworkName(network, 'Onboarding'), ].includes(pool.sender); const getPoolStatus = (pool) => { @@ -107,6 +114,14 @@ async function main() { fetchValidationPool(poolIndex); fetchReputation(); }); + + work1.on('AvailabilityStaked', async () => { + fetchReputation(); + }); + + onboarding.on('AvailabilityStaked', async () => { + fetchReputation(); + }); } main().catch((error) => { diff --git a/ethereum/scripts/contract-config.js b/ethereum/scripts/contract-config.js index 76a829a..53f1ad6 100644 --- a/ethereum/scripts/contract-config.js +++ b/ethereum/scripts/contract-config.js @@ -5,19 +5,19 @@ const networks = { sepolia: '0xaa36a7', }; -const getContractByNetworkName = (networkName, contractName) => { +const getContractAddressByNetworkName = (networkName, contractName) => { const address = contractAddresses[networkName][contractName]; if (!address) throw new Error(`Contract ${contractName} not recognized`); return address; }; -const getContractByChainId = (chainId, contractName) => { +const getContractAddressByChainId = (chainId, contractName) => { const network = Object.entries(networks).find(([, id]) => id === chainId)[0]; if (!network) throw new Error(`Chain ID ${chainId} not recognized`); - return getContractByNetworkName(network, contractName); + return getContractAddressByNetworkName(network, contractName); }; module.exports = { - getContractByChainId, - getContractByNetworkName, + getContractAddressByChainId, + getContractAddressByNetworkName, };