client now verifies hash and signature on api read
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 39s
Details
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 39s
Details
This commit is contained in:
parent
956aa9728e
commit
e6185fb89f
|
@ -3,9 +3,8 @@ 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');
|
||||
const objectHash = require('object-hash');
|
||||
// const { createHash } = require('node:crypto');
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
|
@ -36,7 +35,8 @@ app.post('/write', async (req, res) => {
|
|||
}
|
||||
// Compute content hash
|
||||
const data = { author, content, signature };
|
||||
const hash = createHash('sha256').update(JSON.stringify(data)).digest('base64url');
|
||||
const hash = objectHash(data);
|
||||
|
||||
console.log('write', hash);
|
||||
// Store content
|
||||
db.put(hash, data);
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
"axios": "^1.6.7",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.18.2",
|
||||
"level": "^8.0.1"
|
||||
"level": "^8.0.1",
|
||||
"object-hash": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.56.0",
|
||||
|
@ -2933,6 +2934,14 @@
|
|||
"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==",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
"axios": "^1.6.7",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.18.2",
|
||||
"level": "^8.0.1"
|
||||
"level": "^8.0.1",
|
||||
"object-hash": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.56.0",
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
"dependencies": {
|
||||
"@helia/dag-json": "^3.0.2",
|
||||
"@libp2p/websockets": "^8.0.16",
|
||||
"@metamask/eth-sig-util": "^7.0.1",
|
||||
"@metamask/sdk-react": "^0.16.0",
|
||||
"@multiformats/multiaddr": "^12.2.1",
|
||||
"@tanstack/react-table": "^8.13.2",
|
||||
|
@ -17,8 +18,10 @@
|
|||
"bootstrap": "^5.3.3",
|
||||
"bootswatch": "^5.3.3",
|
||||
"buffer": "^6.0.3",
|
||||
"create-hash": "^1.2.0",
|
||||
"helia": "^4.1.0",
|
||||
"ipfs-core": "^0.18.1",
|
||||
"object-hash": "^3.0.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.10.1",
|
||||
|
@ -5325,6 +5328,156 @@
|
|||
"uint8arrays": "^5.0.2"
|
||||
}
|
||||
},
|
||||
"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/abi-utils/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/abi-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==",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@metamask/abi-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/@metamask/abi-utils/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/@metamask/abi-utils/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/@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/@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/eth-sig-util/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/@metamask/eth-sig-util/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/@metamask/eth-sig-util/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/@metamask/eth-sig-util/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/@metamask/object-multiplex": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@metamask/object-multiplex/-/object-multiplex-1.3.0.tgz",
|
||||
|
@ -20180,6 +20333,14 @@
|
|||
"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==",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
|
||||
|
@ -20895,6 +21056,14 @@
|
|||
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
|
||||
"integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="
|
||||
},
|
||||
"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",
|
||||
|
@ -23275,6 +23444,16 @@
|
|||
"node": "*"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"dependencies": {
|
||||
"@helia/dag-json": "^3.0.2",
|
||||
"@libp2p/websockets": "^8.0.16",
|
||||
"@metamask/eth-sig-util": "^7.0.1",
|
||||
"@metamask/sdk-react": "^0.16.0",
|
||||
"@multiformats/multiaddr": "^12.2.1",
|
||||
"@tanstack/react-table": "^8.13.2",
|
||||
|
@ -19,8 +20,10 @@
|
|||
"bootstrap": "^5.3.3",
|
||||
"bootswatch": "^5.3.3",
|
||||
"buffer": "^6.0.3",
|
||||
"create-hash": "^1.2.0",
|
||||
"helia": "^4.1.0",
|
||||
"ipfs-core": "^0.18.1",
|
||||
"object-hash": "^3.0.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.10.1",
|
||||
|
|
|
@ -3,7 +3,6 @@ import {
|
|||
} from 'react';
|
||||
import { useSDK } from '@metamask/sdk-react';
|
||||
import { Web3 } from 'web3';
|
||||
import axios from 'axios';
|
||||
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Tab from 'react-bootstrap/Tab';
|
||||
|
@ -21,9 +20,10 @@ import Web3Context from './contexts/Web3Context';
|
|||
import DAOArtifact from './assets/DAO.json';
|
||||
import Work1Artifact from './assets/Work1.json';
|
||||
import OnboardingArtifact from './assets/Onboarding.json';
|
||||
import WorkContract from './components/WorkContract';
|
||||
import AddPostModal from './components/AddPostModal';
|
||||
import ViewPostModal from './components/ViewPostModal';
|
||||
import WorkContract from './components/work-contracts/WorkContract';
|
||||
import AddPostModal from './components/posts/AddPostModal';
|
||||
import ViewPostModal from './components/posts/ViewPostModal';
|
||||
import Post from './utils/Post';
|
||||
|
||||
function App() {
|
||||
const {
|
||||
|
@ -252,11 +252,8 @@ function App() {
|
|||
const handleShowAddPost = () => setShowAddPost(true);
|
||||
|
||||
const handleShowViewPost = async (post) => {
|
||||
const res = await axios.get(`/api/read/${post.contentId}`);
|
||||
const { data } = res;
|
||||
// TODO: Verify base64url(sha256(JSON.stringify(data))) = contentId
|
||||
// TODO: Verify data.author = post.author
|
||||
setViewPostContent(data.content);
|
||||
const { content } = await Post.read(post.contentId);
|
||||
setViewPostContent(content);
|
||||
setShowViewPost(true);
|
||||
};
|
||||
|
||||
|
|
|
@ -2,11 +2,10 @@ import { useCallback, useContext, useState } from 'react';
|
|||
import Button from 'react-bootstrap/Button';
|
||||
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';
|
||||
import Web3Context from '../../contexts/Web3Context';
|
||||
import Post from '../../utils/Post';
|
||||
|
||||
function AddPostModal({
|
||||
show, setShow, title, postToBlockchain, onSubmit,
|
||||
|
@ -20,28 +19,21 @@ function AddPostModal({
|
|||
|
||||
const handleSubmit = useCallback(async () => {
|
||||
// Upload content to API
|
||||
// TODO: include metamask signature
|
||||
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);
|
||||
const post = new Post({ content });
|
||||
// Include metamask signature
|
||||
await post.sign(provider, account);
|
||||
// Clear the input and hide the modal
|
||||
setContent('');
|
||||
setShow(false);
|
||||
// Write to API
|
||||
await post.write();
|
||||
// If requested, upload the hash to the blockchain
|
||||
if (postToBlockchain) {
|
||||
// Upload hash to blockchain
|
||||
await DAO.methods.addPost(account, hash).send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
});
|
||||
await post.publish(DAO, account);
|
||||
}
|
||||
// If requested, call callback
|
||||
if (onSubmit) {
|
||||
onSubmit(hash, data);
|
||||
onSubmit(post);
|
||||
}
|
||||
}, [provider, DAO, account, content, setShow, postToBlockchain, onSubmit]);
|
||||
|
|
@ -2,8 +2,8 @@ import { useCallback, useContext, useEffect } from 'react';
|
|||
import { PropTypes } from 'prop-types';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
|
||||
import Web3Context from '../contexts/Web3Context';
|
||||
import WorkContractContext from '../contexts/WorkContractContext';
|
||||
import Web3Context from '../../contexts/Web3Context';
|
||||
import WorkContractContext from '../../contexts/WorkContractContext';
|
||||
|
||||
const getAvailabilityStatus = (stake) => {
|
||||
if (stake.reclaimed) return 'Reclaimed';
|
|
@ -1,7 +1,7 @@
|
|||
import { useMemo } from 'react';
|
||||
import { PropTypes } from 'prop-types';
|
||||
import useList from '../utils/List';
|
||||
import WorkContractContext from '../contexts/WorkContractContext';
|
||||
import useList from '../../utils/List';
|
||||
import WorkContractContext from '../../contexts/WorkContractContext';
|
||||
import AvailabilityStakes from './AvailabilityStakes';
|
||||
import WorkRequests from './WorkRequests';
|
||||
|
|
@ -2,15 +2,15 @@ import {
|
|||
useCallback, useContext, useEffect, useState,
|
||||
} from 'react';
|
||||
import { PropTypes } from 'prop-types';
|
||||
import axios from 'axios';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
|
||||
import Web3 from 'web3';
|
||||
import Web3Context from '../contexts/Web3Context';
|
||||
import useList from '../utils/List';
|
||||
import WorkContractContext from '../contexts/WorkContractContext';
|
||||
import AddPostModal from './AddPostModal';
|
||||
import ViewPostModal from './ViewPostModal';
|
||||
import Web3Context from '../../contexts/Web3Context';
|
||||
import useList from '../../utils/List';
|
||||
import WorkContractContext from '../../contexts/WorkContractContext';
|
||||
import AddPostModal from '../posts/AddPostModal';
|
||||
import ViewPostModal from '../posts/ViewPostModal';
|
||||
import Post from '../../utils/Post';
|
||||
|
||||
const getRequestStatus = (request) => {
|
||||
switch (Number(request.status)) {
|
||||
|
@ -115,8 +115,7 @@ function WorkRequests({
|
|||
setShowEvidenceModal(true);
|
||||
};
|
||||
|
||||
const onSubmitRequest = useCallback(async (hash) => {
|
||||
// TODO: Accept input, upload to API, include hash in contract call
|
||||
const onSubmitRequest = useCallback(async ({ hash }) => {
|
||||
const web3 = new Web3(provider);
|
||||
const priceWei = BigInt(web3.utils.toWei(price, 'ether'));
|
||||
await workContract.methods.requestWork(hash).send({
|
||||
|
@ -126,7 +125,7 @@ function WorkRequests({
|
|||
});
|
||||
}, [provider, workContract, account, price]);
|
||||
|
||||
const onSubmitEvidence = useCallback(async (hash) => {
|
||||
const onSubmitEvidence = useCallback(async ({ hash }) => {
|
||||
await workContract.methods.submitWorkEvidence(currentRequestId, hash).send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
|
@ -134,11 +133,8 @@ function WorkRequests({
|
|||
}, [workContract, account, currentRequestId]);
|
||||
|
||||
const handleShowViewRequestModal = async (request) => {
|
||||
const res = await axios.get(`/api/read/${request.requestContentId}`);
|
||||
const { data } = res;
|
||||
// TODO: Verify base64url(sha256(JSON.stringify(data))) = contentId
|
||||
// TODO: Verify data.author = post.author
|
||||
setViewRequestContent(data.content);
|
||||
const { content } = await Post.read(request.requestContentId);
|
||||
setViewRequestContent(content);
|
||||
setShowViewRequestModal(true);
|
||||
};
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x2D812555F4eF06267406D80E7fA01Ac3288f626c",
|
||||
"Work1": "0x3CAB55d59af095618F2ee539463E33447cfc97BA",
|
||||
"Onboarding": "0xaB0c7Cf9A436978F55831C8EdB67892419ABAE62"
|
||||
"DAO": "0x691Bcb6a8378Cec103BE58Dfa037DC57E6FFf4d1",
|
||||
"Work1": "0xC489CE618A049B413CE0AED9Fc7219a04510ddbb",
|
||||
"Onboarding": "0x3477A098fBFe09aa26693012176baAEa16d9D2DA"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0xa3b15aBD114C2332652A4fD5f9A43B86315E5078",
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
import axios from 'axios';
|
||||
// trailing slash is deliberate, to differentiate this package from the core node module
|
||||
import { Buffer } from 'buffer/';
|
||||
import { recoverPersonalSignature } from '@metamask/eth-sig-util';
|
||||
// import createHash from 'create-hash';
|
||||
import objectHash from 'object-hash';
|
||||
|
||||
// Make Buffer available to recoverPersonalSignature
|
||||
window.Buffer = Buffer;
|
||||
|
||||
class Post {
|
||||
constructor({
|
||||
author, content, signature, hash,
|
||||
}) {
|
||||
this.author = author;
|
||||
this.content = content;
|
||||
this.signature = signature;
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
// Read from API
|
||||
static async read(hash) {
|
||||
const { data: { content, author, signature } } = await axios.get(`/api/read/${hash}`);
|
||||
// Verify hash
|
||||
const derivedHash = objectHash({ author, content, signature });
|
||||
if (hash !== derivedHash) {
|
||||
throw new Error('Hash mismatch');
|
||||
}
|
||||
// Verify signature
|
||||
let recovered;
|
||||
try {
|
||||
recovered = recoverPersonalSignature({ data: content, signature });
|
||||
} catch (e) {
|
||||
throw new Error('Signature error', e);
|
||||
}
|
||||
if (recovered !== author) {
|
||||
throw new Error('Author mismatch');
|
||||
}
|
||||
return new Post({
|
||||
content, author, signature, hash,
|
||||
});
|
||||
}
|
||||
|
||||
// Include MetaMask signature
|
||||
async sign(web3Provider, account) {
|
||||
this.author = account;
|
||||
const msg = `0x${Buffer.from(this.content, 'utf8').toString('hex')}`;
|
||||
this.signature = await web3Provider.request({
|
||||
method: 'personal_sign',
|
||||
params: [msg, account],
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
// Write to API
|
||||
async write() {
|
||||
const data = {
|
||||
author: this.author,
|
||||
content: this.content,
|
||||
signature: this.signature,
|
||||
};
|
||||
const { data: hash } = await axios.post('/api/write', data);
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
// Upload hash to blockchain
|
||||
async publish(DAO, account) {
|
||||
await DAO.methods.addPost(account, this.hash).send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Post;
|
|
@ -1 +1,4 @@
|
|||
SEPOLIA_PRIVATE_KEY=
|
||||
SEPOLIA_PRIVATE_KEY=
|
||||
ETHERSCAN_API_KEY=
|
||||
WORK1_PRICE="0.001"
|
||||
ONBOARDING_PRICE="0.001"
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x2D812555F4eF06267406D80E7fA01Ac3288f626c",
|
||||
"Work1": "0x3CAB55d59af095618F2ee539463E33447cfc97BA",
|
||||
"Onboarding": "0xaB0c7Cf9A436978F55831C8EdB67892419ABAE62"
|
||||
"DAO": "0x691Bcb6a8378Cec103BE58Dfa037DC57E6FFf4d1",
|
||||
"Work1": "0xC489CE618A049B413CE0AED9Fc7219a04510ddbb",
|
||||
"Onboarding": "0x3477A098fBFe09aa26693012176baAEa16d9D2DA"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0xa3b15aBD114C2332652A4fD5f9A43B86315E5078",
|
||||
|
|
|
@ -3,7 +3,11 @@ const fs = require('fs');
|
|||
|
||||
const contractAddresses = require('../contract-addresses.json');
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const network = process.env.HARDHAT_NETWORK;
|
||||
const work1Price = process.env.WORK1_PRICE || 0.001;
|
||||
const onboardingPrice = process.env.ONBOARDING_PRICE || '0.001';
|
||||
|
||||
async function main() {
|
||||
const dao = await ethers.deployContract('DAO');
|
||||
|
@ -21,8 +25,8 @@ async function main() {
|
|||
fs.copyFileSync(`./artifacts/contracts/${name}.sol/${name}.json`, `../client/src/assets/${name}.json`);
|
||||
};
|
||||
|
||||
await deployWorkContract('Work1', ethers.parseEther('0.001'));
|
||||
await deployWorkContract('Onboarding', ethers.parseEther('0.001'));
|
||||
await deployWorkContract('Work1', ethers.parseEther(work1Price));
|
||||
await deployWorkContract('Onboarding', ethers.parseEther(onboardingPrice));
|
||||
|
||||
fs.writeFileSync('../client/src/contract-addresses.json', JSON.stringify(contractAddresses, null, 2));
|
||||
console.log('Wrote file', fs.realpathSync('../client/src/contract-addresses.json'));
|
||||
|
|
Loading…
Reference in New Issue