add multi-author support to frontend

This commit is contained in:
Ladd Hoffman 2024-04-19 18:07:48 -05:00
parent ece57c9706
commit 626905bad6
10 changed files with 116 additions and 54 deletions

View File

@ -1,9 +1,9 @@
{
"localhost": {
"DAO": "0x8d914D38dD301FC4606f5aa9fEcF8A76389020d3",
"Work1": "0xfe58B9EB03F75A603de1B286584f5E9532ab8fB5",
"Onboarding": "0x1d63FDe5B461106729fE1e5e38A02fc68C518Af5",
"Proposals": "0x050C420Cc4995B41217Eba1B54B82Fd5687e9139"
"DAO": "0xD60A1c64B96a133587A75C2771690072F238a549",
"Work1": "0xCF3f16D151052FA7b99a71E79EC3b0e6C793aa0b",
"Onboarding": "0xE148e864A646B8bFc95dcc9acd3dBcB52704EE60",
"Proposals": "0x981234BBBC1ec93200F5BB3a65e2F9711A6109aa"
},
"sepolia": {
"DAO": "0x241514DC94568e98222fBE66662b054b545A61AE",

View File

@ -82,9 +82,10 @@ function App() {
}, [DAORef, account]);
const fetchPost = useCallback(async (postId) => {
const p = await DAORef.current.methods.posts(postId).call();
dispatchPost({ type: 'updateById', item: p });
return p;
const post = await DAORef.current.methods.posts(postId).call();
post.authors = await DAORef.current.methods.getPostAuthors(postId).call();
dispatchPost({ type: 'updateById', item: post });
return post;
}, [DAORef, dispatchPost]);
const fetchPostId = useCallback(async (postIndex) => {
@ -186,14 +187,14 @@ function App() {
setWork1(Work1Contract);
setOnboarding(OnboardingContract);
const fetchReputationInterval = setInterval(() => {
console.log('reputation', reputation);
if (reputation !== undefined) {
clearInterval(fetchReputationInterval);
return;
}
fetchReputation();
}, 1000);
// const fetchReputationInterval = setInterval(() => {
// // console.log('reputation', reputation);
// if (reputation !== undefined) {
// clearInterval(fetchReputationInterval);
// return;
// }
// fetchReputation();
// }, 1000);
/* -------------------------------------------------------------------------------- */
/* --------------------------- BEGIN EVENT HANDLERS ------------------------------- */
@ -414,7 +415,7 @@ function App() {
<thead>
<tr>
<th>ID</th>
<th>Author</th>
<th>Authors</th>
<th>Sender</th>
<th>Actions</th>
</tr>
@ -423,7 +424,18 @@ function App() {
{posts.filter((x) => !!x).map((post) => (
<tr key={post.id}>
<td>{post.id.toString()}</td>
<td>{getAddressName(chainId, post.author)}</td>
<td>
<Stack>
{post.authors.map(({ authorAddress, weightPercent }) => (
<div key={authorAddress}>
{getAddressName(chainId, authorAddress)}
{' '}
{weightPercent.toString()}
%
</div>
))}
</Stack>
</td>
<td>{getAddressName(chainId, post.sender)}</td>
<td>
<Button onClick={() => handleShowViewPost(post)}>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -19,7 +19,7 @@ function AddPostModal({
const handleSubmit = useCallback(async () => {
// Upload content to API
const post = new Post({ content });
const post = new Post({ content, authors: [{ weightPercent: 100, authorAddress: account }] });
// Include metamask signature
await post.sign(provider, account);
// Clear the input and hide the modal

View File

@ -1,12 +1,13 @@
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import Stack from 'react-bootstrap/Stack';
import PropTypes from 'prop-types';
function ViewPostModal({
show, setShow, title, post,
}) {
const handleClose = () => setShow(false);
const { content, author, embeddedData } = post;
const { content, authors, embeddedData } = post;
const embeddedDataJson = JSON.stringify(embeddedData, null, 2);
@ -19,9 +20,18 @@ function ViewPostModal({
</Modal.Header>
<Modal.Body>
<h6>
Author:
{' '}
{author}
Authors:
<Stack>
{authors?.map(({ authorAddress, weightPercent }) => (
<div key={authorAddress}>
{authorAddress}
{' '}
{weightPercent.toString()}
%
</div>
))}
</Stack>
</h6>
<hr />
<p className="post-content">

View File

@ -9,25 +9,26 @@ window.Buffer = Buffer;
class Post {
constructor({
author, content, signature, hash, embeddedData,
authors, content, signature, hash, embeddedData, citations,
}) {
this.author = author;
this.authors = authors;
this.content = content;
this.signature = signature;
this.hash = hash;
this.embeddedData = embeddedData;
this.citations = citations;
}
// Read from API
static async read(hash) {
const {
data: {
content, author, signature, embeddedData,
content, authors, signature, embeddedData, citations,
},
} = await axios.get(`/api/read/${hash}`);
// Verify hash
const derivedHash = objectHash({
author, content, signature, embeddedData,
authors, content, signature, embeddedData,
});
if (hash !== derivedHash) {
throw new Error('Hash mismatch');
@ -38,12 +39,12 @@ class Post {
contentToVerify += `\n\n${JSON.stringify(embeddedData, null, 2)}`;
}
const recovered = recoverPersonalSignature({ data: contentToVerify, signature });
if (recovered !== author) {
throw new Error('Author mismatch');
const authorAddresses = authors.map((author) => author.authorAddress);
if (!authorAddresses.includes(recovered)) {
throw new Error('Signer is not among the authors');
}
return new Post({
content, author, signature, hash, embeddedData,
content, authors, signature, hash, embeddedData, citations,
});
}
@ -56,10 +57,13 @@ class Post {
// Include MetaMask signature
async sign(web3Provider, account) {
this.author = account;
const author = this.authors?.find(({ authorAddress }) => authorAddress === account);
if (!author) {
throw new Error('Post must be signed by one of its authors');
}
let contentToSign = this.content;
if (this.embeddedData && Object.entries(this.embeddedData).length) {
contentToSign += `\n\n${JSON.stringify(this.embeddedData, null, 2)}`;
contentToSign += `\n\nDATA\n${JSON.stringify(this.embeddedData, null, 2)}`;
}
const msg = `0x${Buffer.from(contentToSign, 'utf8').toString('hex')}`;
this.signature = await web3Provider.request({
@ -72,10 +76,11 @@ class Post {
// Write to API
async write() {
const data = {
author: this.author,
authors: this.authors,
content: this.content,
signature: this.signature,
embeddedData: this.embeddedData,
citations: this.citations,
};
const { data: hash } = await axios.post('/api/write', data);
this.hash = hash;
@ -83,10 +88,7 @@ class Post {
// Upload hash to blockchain
async publish(DAO, account) {
await DAO.methods.addPost([{
weightPercent: 100,
authorAddress: account,
}], this.hash, []).send({
await DAO.methods.addPost(this.authors, this.hash, this.citations ?? []).send({
from: account,
gas: 1000000,
});

View File

@ -1,4 +1,4 @@
import contractAddresses from '../contract-addresses.json';
import contractAddresses from '../../contract-addresses.json';
const networks = {
localhost: '0x539',