frontend: refactor to consolidate main tabs as a component
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 30s
Details
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 30s
Details
This commit is contained in:
parent
2c5fb00180
commit
0843a3279d
|
@ -1,9 +1,11 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x8d914D38dD301FC4606f5aa9fEcF8A76389020d3",
|
||||
"Work1": "0xfe58B9EB03F75A603de1B286584f5E9532ab8fB5",
|
||||
"Onboarding": "0x1d63FDe5B461106729fE1e5e38A02fc68C518Af5",
|
||||
"Proposals": "0x050C420Cc4995B41217Eba1B54B82Fd5687e9139"
|
||||
"DAO": "0x431c73b38B9eBe4a30C7A0ffa11188fa9429Bf38",
|
||||
"Work1": "0x0173903444ed92F5e30f522d0f274162c57b4Cc0",
|
||||
"Onboarding": "0x44df3b45C937c88267999a5f69D9BFBe83dEf447",
|
||||
"Proposals": "0x663f119ffb7eD161A00C89c25046Afe8B40fff52",
|
||||
"Rollup": "0xB61bB0defB5A0B59F0BAeBF2F4345d7c317c20B4",
|
||||
"Work2": "0x4774670f82A590e7eD6072bd7098836B06FFd8ce"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0x8e5bd58B2ca8910C5F9be8de847d6883B15c60d2",
|
||||
|
|
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,9 +1,11 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x8d914D38dD301FC4606f5aa9fEcF8A76389020d3",
|
||||
"Work1": "0xfe58B9EB03F75A603de1B286584f5E9532ab8fB5",
|
||||
"Onboarding": "0x1d63FDe5B461106729fE1e5e38A02fc68C518Af5",
|
||||
"Proposals": "0x050C420Cc4995B41217Eba1B54B82Fd5687e9139"
|
||||
"DAO": "0x431c73b38B9eBe4a30C7A0ffa11188fa9429Bf38",
|
||||
"Work1": "0x0173903444ed92F5e30f522d0f274162c57b4Cc0",
|
||||
"Onboarding": "0x44df3b45C937c88267999a5f69D9BFBe83dEf447",
|
||||
"Proposals": "0x663f119ffb7eD161A00C89c25046Afe8B40fff52",
|
||||
"Rollup": "0xB61bB0defB5A0B59F0BAeBF2F4345d7c317c20B4",
|
||||
"Work2": "0x4774670f82A590e7eD6072bd7098836B06FFd8ce"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0x8e5bd58B2ca8910C5F9be8de847d6883B15c60d2",
|
||||
|
|
|
@ -10,8 +10,8 @@ abstract contract RollableWork is Work {
|
|||
constructor(
|
||||
DAO dao,
|
||||
Proposals proposalsContract,
|
||||
uint price,
|
||||
Rollup rollupContract_
|
||||
Rollup rollupContract_,
|
||||
uint price
|
||||
) Work(dao, proposalsContract, price) {
|
||||
rollupContract = rollupContract_;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: Unlicense
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "./core/DAO.sol";
|
||||
import "./RollableWork.sol";
|
||||
import "./Proposals.sol";
|
||||
import "./Rollup.sol";
|
||||
|
||||
contract Work2 is RollableWork {
|
||||
constructor(
|
||||
DAO dao,
|
||||
Proposals proposals,
|
||||
Rollup rollup,
|
||||
uint price
|
||||
) RollableWork(dao, proposals, rollup, price) {}
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
const deployWorkContract = require('./util/deploy-work-contract');
|
||||
const deployContract = require('./util/deploy-contract');
|
||||
const deployDAOContract = require('./util/deploy-dao-contract');
|
||||
const deployWorkContract = require('./util/deploy-work-contract');
|
||||
const deployRollableWorkContract = require('./util/deploy-rollable-work-contract');
|
||||
|
||||
async function main() {
|
||||
await deployContract('DAO', [], true);
|
||||
await deployDAOContract('Rollup');
|
||||
await deployDAOContract('Proposals');
|
||||
await deployWorkContract('Work1');
|
||||
await deployWorkContract('Onboarding');
|
||||
await deployRollableWorkContract('Work2');
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
const { ethers } = require('hardhat');
|
||||
const deployContract = require('./deploy-contract');
|
||||
|
||||
const contractAddresses = require('../../contract-addresses.json');
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const network = process.env.HARDHAT_NETWORK;
|
||||
|
||||
const deployRollableWorkContract = async (name) => {
|
||||
const priceEnvVar = `${name.toUpperCase()}_PRICE`;
|
||||
const price = ethers.parseEther(process.env[priceEnvVar] || '0.001');
|
||||
|
||||
await deployContract(name, [
|
||||
contractAddresses[network].DAO,
|
||||
contractAddresses[network].Proposals,
|
||||
contractAddresses[network].Rollup,
|
||||
price,
|
||||
]);
|
||||
};
|
||||
|
||||
module.exports = deployRollableWorkContract;
|
|
@ -9,7 +9,7 @@ const network = process.env.HARDHAT_NETWORK;
|
|||
|
||||
const deployWorkContract = async (name) => {
|
||||
const priceEnvVar = `${name.toUpperCase()}_PRICE`;
|
||||
const price = ethers.parseEther(process.env[priceEnvVar] || 0.001);
|
||||
const price = ethers.parseEther(process.env[priceEnvVar] || '0.001');
|
||||
|
||||
await deployContract(name, [
|
||||
contractAddresses[network].DAO,
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x8d914D38dD301FC4606f5aa9fEcF8A76389020d3",
|
||||
"Work1": "0xfe58B9EB03F75A603de1B286584f5E9532ab8fB5",
|
||||
"Onboarding": "0x1d63FDe5B461106729fE1e5e38A02fc68C518Af5",
|
||||
"Proposals": "0x050C420Cc4995B41217Eba1B54B82Fd5687e9139"
|
||||
"DAO": "0x431c73b38B9eBe4a30C7A0ffa11188fa9429Bf38",
|
||||
"Work1": "0x0173903444ed92F5e30f522d0f274162c57b4Cc0",
|
||||
"Onboarding": "0x44df3b45C937c88267999a5f69D9BFBe83dEf447",
|
||||
"Proposals": "0x663f119ffb7eD161A00C89c25046Afe8B40fff52",
|
||||
"Rollup": "0xB61bB0defB5A0B59F0BAeBF2F4345d7c317c20B4",
|
||||
"Work2": "0x4774670f82A590e7eD6072bd7098836B06FFd8ce"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0x8e5bd58B2ca8910C5F9be8de847d6883B15c60d2",
|
||||
|
|
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,263 +1,27 @@
|
|||
import {
|
||||
useCallback, useEffect, useState, useMemo, useRef,
|
||||
} from 'react';
|
||||
import { useSDK } from '@metamask/sdk-react';
|
||||
import { Web3 } from 'web3';
|
||||
import { useCallback } from 'react';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Tab from 'react-bootstrap/Tab';
|
||||
import Tabs from 'react-bootstrap/Tabs';
|
||||
import Container from 'react-bootstrap/Container';
|
||||
import Row from 'react-bootstrap/Row';
|
||||
import Col from 'react-bootstrap/Col';
|
||||
import Stack from 'react-bootstrap/Stack';
|
||||
|
||||
import useList from './utils/List';
|
||||
import { getContractAddressByChainId } from './utils/contract-config';
|
||||
import Web3Context from './contexts/Web3Context';
|
||||
import DAOArtifact from '../contractArtifacts/DAO.json';
|
||||
import Work1Artifact from '../contractArtifacts/Work1.json';
|
||||
import OnboardingArtifact from '../contractArtifacts/Onboarding.json';
|
||||
import WorkContract from './components/work-contracts/WorkContract';
|
||||
import AddPostModal from './components/posts/AddPostModal';
|
||||
import ViewPostModal from './components/posts/ViewPostModal';
|
||||
import Post from './utils/Post';
|
||||
import Proposals from './components/Proposals';
|
||||
import ImportPaper from './components/ImportPaper';
|
||||
import ImportPapersByAuthor from './components/ImportPapersByAuthor';
|
||||
import getAddressName from './utils/get-address-name';
|
||||
import ImportMatrixEvent from './components/ImportMatrixEvent';
|
||||
import MainTabs from './components/tabs';
|
||||
import useMainContext from './contexts/useMainContext';
|
||||
|
||||
function WebApp() {
|
||||
const {
|
||||
sdk, connected, provider, chainId, account, balance,
|
||||
} = useSDK();
|
||||
|
||||
const DAORef = useRef();
|
||||
const workRef = useRef();
|
||||
const onboardingRef = useRef();
|
||||
const [DAO, setDAO] = useState();
|
||||
const [work1, setWork1] = useState();
|
||||
const [onboarding, setOnboarding] = useState();
|
||||
const [balanceEther, setBalanceEther] = useState();
|
||||
const [reputation, setReputation] = useState();
|
||||
const [totalReputation, setTotalReputation] = useState();
|
||||
const [members, dispatchMember] = useList();
|
||||
const [posts, dispatchPost] = useList();
|
||||
const [validationPools, dispatchValidationPool] = useList();
|
||||
const [showAddPost, setShowAddPost] = useState(false);
|
||||
const [showViewPost, setShowViewPost] = useState(false);
|
||||
const [viewPost, setViewPost] = useState({});
|
||||
const web3ProviderValue = useMemo(() => ({
|
||||
provider,
|
||||
DAO,
|
||||
work1,
|
||||
onboarding,
|
||||
reputation,
|
||||
setReputation,
|
||||
account,
|
||||
chainId,
|
||||
posts,
|
||||
DAORef,
|
||||
workRef,
|
||||
onboardingRef,
|
||||
}), [
|
||||
provider, DAO, work1, onboarding, reputation, setReputation, account, chainId, posts,
|
||||
DAORef, workRef, onboardingRef]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!provider || balance === undefined) return;
|
||||
const web3 = new Web3(provider);
|
||||
setBalanceEther(web3.utils.fromWei(balance, 'ether'));
|
||||
}, [provider, balance]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- BEGIN FETCHERS ------------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
const fetchReputation = useCallback(async () => {
|
||||
setReputation(await DAORef.current.methods.balanceOf(account).call());
|
||||
setTotalReputation(await DAORef.current.methods.totalSupply().call());
|
||||
}, [DAORef, account]);
|
||||
|
||||
const fetchPost = useCallback(async (postId) => {
|
||||
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) => {
|
||||
const postId = await DAORef.current.methods.postIds(postIndex).call();
|
||||
return postId;
|
||||
}, [DAORef]);
|
||||
|
||||
const fetchPosts = useCallback(async () => {
|
||||
const count = await DAORef.current.methods.postCount().call();
|
||||
let promises = [];
|
||||
dispatchPost({ type: 'refresh' });
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchPostId(i));
|
||||
}
|
||||
const postIds = await Promise.all(promises);
|
||||
promises = [];
|
||||
postIds.forEach((postId) => {
|
||||
promises.push(fetchPost(postId));
|
||||
});
|
||||
}, [DAORef, dispatchPost, fetchPost, fetchPostId]);
|
||||
|
||||
const fetchValidationPool = useCallback(async (poolIndex) => {
|
||||
const getPoolStatus = (pool) => {
|
||||
if (pool.resolved) {
|
||||
return pool.outcome ? 'Accepted' : 'Rejected';
|
||||
}
|
||||
return pool.timeRemaining > 0 ? 'In Progress' : 'Ready to Evaluate';
|
||||
};
|
||||
const pool = await DAORef.current.methods.validationPools(poolIndex).call();
|
||||
pool.id = Number(pool.id);
|
||||
pool.timeRemaining = new Date(Number(pool.endTime) * 1000) - new Date();
|
||||
pool.status = getPoolStatus(pool);
|
||||
dispatchValidationPool({ type: 'update', item: pool });
|
||||
|
||||
// When remaing time expires, we want to update the status for this pool
|
||||
if (pool.timeRemaining > 0) {
|
||||
setTimeout(() => {
|
||||
pool.timeRemaining = 0;
|
||||
pool.status = getPoolStatus(pool);
|
||||
dispatchValidationPool({ type: 'update', item: pool });
|
||||
}, pool.timeRemaining);
|
||||
}
|
||||
}, [DAORef, dispatchValidationPool]);
|
||||
|
||||
const fetchValidationPools = useCallback(async () => {
|
||||
// TODO: Pagination
|
||||
// TODO: Memoization
|
||||
// TODO: Caching
|
||||
const count = await DAORef.current.methods.validationPoolCount().call();
|
||||
const promises = [];
|
||||
dispatchValidationPool({ type: 'refresh' });
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchValidationPool(i));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}, [DAORef, dispatchValidationPool, fetchValidationPool]);
|
||||
|
||||
const fetchMember = useCallback(async (memberIndex) => {
|
||||
const id = await DAORef.current.methods.members(memberIndex).call();
|
||||
const member = { id };
|
||||
member.reputation = await DAORef.current.methods.balanceOf(id).call();
|
||||
dispatchMember({ type: 'updateById', item: member });
|
||||
return member;
|
||||
}, [DAORef, dispatchMember]);
|
||||
|
||||
const fetchMembers = useCallback(async () => {
|
||||
const count = await DAORef.current.methods.memberCount().call();
|
||||
const promises = [];
|
||||
dispatchMember({ type: 'refresh' });
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchMember(i));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}, [DAORef, dispatchMember, fetchMember]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- END FETCHERS --------------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
// In this effect, we initialize everything and add contract event listeners.
|
||||
useEffect(() => {
|
||||
if (!provider || !chainId || !account || balance === undefined) return () => {};
|
||||
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);
|
||||
const OnboardingContract = new web3.eth.Contract(OnboardingArtifact.abi, OnboardingAddress);
|
||||
DAORef.current = DAOContract;
|
||||
workRef.current = Work1Contract;
|
||||
onboardingRef.current = OnboardingContract;
|
||||
|
||||
fetchReputation();
|
||||
fetchMembers();
|
||||
fetchPosts();
|
||||
fetchValidationPools();
|
||||
|
||||
setDAO(DAOContract);
|
||||
setWork1(Work1Contract);
|
||||
setOnboarding(OnboardingContract);
|
||||
|
||||
// const fetchReputationInterval = setInterval(() => {
|
||||
// // console.log('reputation', reputation);
|
||||
// if (reputation !== undefined) {
|
||||
// clearInterval(fetchReputationInterval);
|
||||
// return;
|
||||
// }
|
||||
// fetchReputation();
|
||||
// }, 1000);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- BEGIN EVENT HANDLERS ------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
DAOContract.events.PostAdded({ fromBlock: 'latest' }).on('data', (event) => {
|
||||
console.log('event: post added');
|
||||
fetchPost(event.returnValues.id);
|
||||
});
|
||||
|
||||
DAOContract.events.ValidationPoolInitiated({ fromBlock: 'latest' }).on('data', (event) => {
|
||||
console.log('event: validation pool initiated');
|
||||
fetchValidationPool(event.returnValues.poolIndex);
|
||||
});
|
||||
|
||||
DAOContract.events.ValidationPoolResolved({ fromBlock: 'latest' }).on('data', (event) => {
|
||||
console.log('event: validation pool resolved');
|
||||
fetchReputation();
|
||||
fetchValidationPool(event.returnValues.poolIndex);
|
||||
fetchMembers();
|
||||
});
|
||||
|
||||
Work1Contract.events.AvailabilityStaked({ fromBlock: 'latest' }).on('data', () => {
|
||||
fetchReputation();
|
||||
});
|
||||
|
||||
OnboardingContract.events.AvailabilityStaked({ fromBlock: 'latest' }).on('data', () => {
|
||||
fetchReputation();
|
||||
});
|
||||
|
||||
return () => {
|
||||
DAOContract.events.PostAdded().off();
|
||||
DAOContract.events.ValidationPoolInitiated().off();
|
||||
DAOContract.events.ValidationPoolResolved().off();
|
||||
Work1Contract.events.AvailabilityStaked().off();
|
||||
OnboardingContract.events.AvailabilityStaked().off();
|
||||
};
|
||||
}, [provider, account, chainId, balance, dispatchValidationPool, dispatchPost,
|
||||
DAORef, workRef, onboardingRef,
|
||||
fetchPost, fetchPosts, fetchReputation, fetchValidationPool, fetchValidationPools, fetchMembers,
|
||||
]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- END MAIN INITIALIZION EFFECT ----------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
// useEffect(() => {
|
||||
// // api.requestCapability(MatrixCapabilities.)
|
||||
// api.on('action:m.message', (ev) => {
|
||||
// console.log('action:m.message', ev);
|
||||
// });
|
||||
// api.start();
|
||||
// }, []);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- BEGIN UI ACTIONS ----------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
sdk, connected, chainId, account, reputation, totalReputation, balanceEther,
|
||||
} = useMainContext();
|
||||
|
||||
const connect = useCallback(async () => {
|
||||
try {
|
||||
await sdk?.connect();
|
||||
console.log('connected');
|
||||
console.log('connected', connected);
|
||||
} catch (err) {
|
||||
console.warn('failed to connect..', err);
|
||||
}
|
||||
}, [sdk]);
|
||||
}, [sdk, connected]);
|
||||
|
||||
const disconnect = useCallback(async () => {
|
||||
try {
|
||||
|
@ -267,67 +31,8 @@ function WebApp() {
|
|||
}
|
||||
}, [sdk]);
|
||||
|
||||
const initiateValidationPool = useCallback(async (postId, poolDuration) => {
|
||||
const web3 = new Web3(provider);
|
||||
await DAO.methods.initiateValidationPool(
|
||||
postId,
|
||||
poolDuration ?? 3600,
|
||||
[1, 3],
|
||||
[1, 2],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
web3.eth.abi.encodeParameter('bytes', '0x00'),
|
||||
).send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
value: 10000,
|
||||
});
|
||||
}, [provider, DAO, account]);
|
||||
|
||||
const stake = useCallback(async (poolIndex, amount, inFavor) => {
|
||||
console.log(`Attempting to stake ${amount} ${inFavor ? 'for' : 'against'} pool ${poolIndex}`);
|
||||
await DAO.methods.stakeOnValidationPool(poolIndex, amount, inFavor).send({
|
||||
from: account,
|
||||
gas: 999999,
|
||||
});
|
||||
|
||||
// Since this is the result we expect from the server, we preemptively set it here.
|
||||
// We can let this value be negative -- this would just mean we'll be getting
|
||||
// at least one error from the server, and a corrected reputation.
|
||||
setReputation((current) => current - BigInt(amount));
|
||||
}, [DAO, account, setReputation]);
|
||||
|
||||
const stakeHalfInFavor = useCallback(async (poolIndex) => {
|
||||
await stake(poolIndex, reputation / BigInt(2), true);
|
||||
}, [stake, reputation]);
|
||||
|
||||
const evaluateOutcome = useCallback(async (poolIndex) => {
|
||||
await DAO.methods.evaluateOutcome(poolIndex).send({
|
||||
from: account,
|
||||
gas: 10000000,
|
||||
});
|
||||
}, [DAO, account]);
|
||||
|
||||
const handleShowAddPost = () => setShowAddPost(true);
|
||||
|
||||
const handleShowViewPost = useCallback(async ({ id }) => {
|
||||
const post = await Post.read(id);
|
||||
setViewPost(post);
|
||||
setShowViewPost(true);
|
||||
}, [setViewPost, setShowViewPost]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- END UI ACTIONS ------------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
return (
|
||||
<Web3Context.Provider value={web3ProviderValue}>
|
||||
|
||||
<AddPostModal show={showAddPost} setShow={setShowAddPost} postToBlockchain />
|
||||
|
||||
<ViewPostModal show={showViewPost} setShow={setShowViewPost} post={viewPost} />
|
||||
|
||||
<>
|
||||
{!connected && <Button onClick={() => connect()}>Connect</Button>}
|
||||
|
||||
{connected && (
|
||||
|
@ -372,211 +77,11 @@ function WebApp() {
|
|||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
<Tabs>
|
||||
<Tab eventKey="admin" title="Admin">
|
||||
<h2>Members</h2>
|
||||
<div>
|
||||
{`Members count: ${members.length}`}
|
||||
</div>
|
||||
<div>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Reputation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{members.filter((x) => !!x).map((member) => (
|
||||
<tr key={member.id}>
|
||||
<td>{member.id}</td>
|
||||
<td>{member.reputation.toString()}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{' '}
|
||||
<h2>Posts</h2>
|
||||
<div>
|
||||
<Button onClick={handleShowAddPost}>Add Post</Button>
|
||||
</div>
|
||||
<div>
|
||||
{`Posts count: ${posts.length}`}
|
||||
</div>
|
||||
<div>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Authors</th>
|
||||
<th>Sender</th>
|
||||
<th>Reputation</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{posts.filter((x) => !!x).map((post) => (
|
||||
<tr key={post.id}>
|
||||
<td>{post.id.toString()}</td>
|
||||
<td>
|
||||
<Stack>
|
||||
{post.authors.map(({ authorAddress, weightPPM }) => (
|
||||
<div key={authorAddress}>
|
||||
{getAddressName(chainId, authorAddress)}
|
||||
{' '}
|
||||
{Number(weightPPM) / 10000}
|
||||
%
|
||||
</div>
|
||||
))}
|
||||
</Stack>
|
||||
</td>
|
||||
<td>{getAddressName(chainId, post.sender)}</td>
|
||||
<td>{post.reputation.toString()}</td>
|
||||
<td>
|
||||
<Button onClick={() => handleShowViewPost(post)}>
|
||||
View Post
|
||||
</Button>
|
||||
{' '}
|
||||
Initiate Validation Pool
|
||||
{' '}
|
||||
<Button onClick={() => initiateValidationPool(post.id, 1)}>
|
||||
1s
|
||||
</Button>
|
||||
{' '}
|
||||
<Button onClick={() => initiateValidationPool(post.id, 20)}>
|
||||
20s
|
||||
</Button>
|
||||
{' '}
|
||||
<Button onClick={() => initiateValidationPool(post.id, 60)}>
|
||||
60s
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h2>Validation Pools</h2>
|
||||
<div>
|
||||
{`Validation Pool Count: ${validationPools.length}`}
|
||||
</div>
|
||||
<div>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Post ID</th>
|
||||
<th>Sender</th>
|
||||
<th>Fee</th>
|
||||
<th>Binding</th>
|
||||
<th>Quorum</th>
|
||||
<th>WinRatio</th>
|
||||
<th>
|
||||
Redistribute
|
||||
<br />
|
||||
Losing Stakes
|
||||
</th>
|
||||
<th>Duration</th>
|
||||
<th>End Time</th>
|
||||
<th>
|
||||
Stake
|
||||
<br />
|
||||
Count
|
||||
</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{validationPools.filter((x) => !!x).map((pool) => (
|
||||
<tr key={pool.id}>
|
||||
<td>{pool.id.toString()}</td>
|
||||
<td>{pool.postId}</td>
|
||||
<td>{getAddressName(chainId, pool.sender)}</td>
|
||||
<td>{pool.fee.toString()}</td>
|
||||
<td>
|
||||
{pool.params.bindingPercent.toString()}
|
||||
%
|
||||
</td>
|
||||
<td>{`${pool.params.quorum[0].toString()}/${pool.params.quorum[1].toString()}`}</td>
|
||||
<td>{`${pool.params.winRatio[0].toString()}/${pool.params.winRatio[1].toString()}`}</td>
|
||||
<td>{pool.params.redistributeLosingStakes.toString()}</td>
|
||||
<td>{pool.params.duration.toString()}</td>
|
||||
<td>{new Date(Number(pool.endTime) * 1000).toLocaleString()}</td>
|
||||
<td>{pool.stakeCount.toString()}</td>
|
||||
<td>{pool.status}</td>
|
||||
<td>
|
||||
{!pool.resolved && reputation > 0 && pool.timeRemaining > 0 && (
|
||||
<>
|
||||
<Button onClick={() => stakeHalfInFavor(pool.id)}>
|
||||
Stake 1/2 REP
|
||||
</Button>
|
||||
{' '}
|
||||
<Button onClick={() => stake(pool.id, reputation, true)}>
|
||||
Stake All
|
||||
</Button>
|
||||
{' '}
|
||||
</>
|
||||
)}
|
||||
{!pool.resolved && (pool.timeRemaining <= 0 || !reputation) && (
|
||||
<Button onClick={() => evaluateOutcome(pool.id)}>
|
||||
Evaluate Outcome
|
||||
</Button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab eventKey="worker" title="Worker">
|
||||
{work1 && (
|
||||
<WorkContract
|
||||
workContract={work1}
|
||||
title="Work Contract 1"
|
||||
verb="Work"
|
||||
showProposePriceChange
|
||||
/>
|
||||
)}
|
||||
{onboarding && (
|
||||
<WorkContract
|
||||
workContract={onboarding}
|
||||
title="Onboarding"
|
||||
verb="Onboarding"
|
||||
showRequestWork
|
||||
showProposePriceChange
|
||||
/>
|
||||
)}
|
||||
</Tab>
|
||||
<Tab eventKey="customer" title="Customer">
|
||||
{work1 && (
|
||||
<WorkContract
|
||||
workContract={work1}
|
||||
showAvailabilityActions={false}
|
||||
showAvailabilityAmount={false}
|
||||
onlyShowAvailable
|
||||
title="Work Contract 1"
|
||||
verb="Work"
|
||||
showRequestWork
|
||||
/>
|
||||
)}
|
||||
</Tab>
|
||||
<Tab eventKey="proposals" title="Proposals">
|
||||
<Proposals />
|
||||
</Tab>
|
||||
<Tab eventKey="import" title="Import">
|
||||
<h1>Semantic Scholar</h1>
|
||||
<ImportPaper />
|
||||
<ImportPapersByAuthor />
|
||||
<h1>Matrix</h1>
|
||||
<ImportMatrixEvent />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<MainTabs />
|
||||
</>
|
||||
)}
|
||||
</Web3Context.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import {
|
||||
useCallback, useEffect, useState, useMemo, useRef,
|
||||
useCallback, useEffect, useRef,
|
||||
} from 'react';
|
||||
import { useSDK } from '@metamask/sdk-react';
|
||||
import { Web3 } from 'web3';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Tab from 'react-bootstrap/Tab';
|
||||
import Tabs from 'react-bootstrap/Tabs';
|
||||
import Container from 'react-bootstrap/Container';
|
||||
import Row from 'react-bootstrap/Row';
|
||||
import Col from 'react-bootstrap/Col';
|
||||
|
@ -14,234 +10,15 @@ import {
|
|||
WidgetApi, EventDirection, WidgetEventCapability,
|
||||
} from 'matrix-widget-api';
|
||||
|
||||
import useList from './utils/List';
|
||||
import { getContractAddressByChainId } from './utils/contract-config';
|
||||
import Web3Context from './contexts/Web3Context';
|
||||
import DAOArtifact from '../contractArtifacts/DAO.json';
|
||||
import Work1Artifact from '../contractArtifacts/Work1.json';
|
||||
import OnboardingArtifact from '../contractArtifacts/Onboarding.json';
|
||||
import WorkContract from './components/work-contracts/WorkContract';
|
||||
import AddPostModal from './components/posts/AddPostModal';
|
||||
import ViewPostModal from './components/posts/ViewPostModal';
|
||||
import Post from './utils/Post';
|
||||
import Proposals from './components/Proposals';
|
||||
import ImportPaper from './components/ImportPaper';
|
||||
import ImportPapersByAuthor from './components/ImportPapersByAuthor';
|
||||
import getAddressName from './utils/get-address-name';
|
||||
import ImportMatrixEvent from './components/ImportMatrixEvent';
|
||||
import MainTabs from './components/tabs';
|
||||
import useMainContext from './contexts/useMainContext';
|
||||
|
||||
function Widget() {
|
||||
const {
|
||||
sdk, connected, provider, chainId, account, balance,
|
||||
} = useSDK();
|
||||
sdk, connected, provider, chainId, account, balanceEther, reputation, totalReputation,
|
||||
} = useMainContext();
|
||||
|
||||
const DAORef = useRef();
|
||||
const workRef = useRef();
|
||||
const onboardingRef = useRef();
|
||||
const [DAO, setDAO] = useState();
|
||||
const [work1, setWork1] = useState();
|
||||
const [onboarding, setOnboarding] = useState();
|
||||
const [balanceEther, setBalanceEther] = useState();
|
||||
const [reputation, setReputation] = useState();
|
||||
const [totalReputation, setTotalReputation] = useState();
|
||||
const [members, dispatchMember] = useList();
|
||||
const [posts, dispatchPost] = useList();
|
||||
const [validationPools, dispatchValidationPool] = useList();
|
||||
const [showAddPost, setShowAddPost] = useState(false);
|
||||
const [showViewPost, setShowViewPost] = useState(false);
|
||||
const [viewPost, setViewPost] = useState({});
|
||||
const widgetApi = useRef();
|
||||
const web3ProviderValue = useMemo(() => ({
|
||||
provider,
|
||||
DAO,
|
||||
work1,
|
||||
onboarding,
|
||||
reputation,
|
||||
setReputation,
|
||||
account,
|
||||
chainId,
|
||||
posts,
|
||||
DAORef,
|
||||
workRef,
|
||||
onboardingRef,
|
||||
}), [
|
||||
provider, DAO, work1, onboarding, reputation, setReputation, account, chainId, posts,
|
||||
DAORef, workRef, onboardingRef]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!provider || balance === undefined) return;
|
||||
const web3 = new Web3(provider);
|
||||
setBalanceEther(web3.utils.fromWei(balance, 'ether'));
|
||||
}, [provider, balance]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- BEGIN FETCHERS ------------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
const fetchReputation = useCallback(async () => {
|
||||
setReputation(await DAORef.current.methods.balanceOf(account).call());
|
||||
setTotalReputation(await DAORef.current.methods.totalSupply().call());
|
||||
}, [DAORef, account]);
|
||||
|
||||
const fetchPost = useCallback(async (postId) => {
|
||||
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) => {
|
||||
const postId = await DAORef.current.methods.postIds(postIndex).call();
|
||||
return postId;
|
||||
}, [DAORef]);
|
||||
|
||||
const fetchPosts = useCallback(async () => {
|
||||
const count = await DAORef.current.methods.postCount().call();
|
||||
let promises = [];
|
||||
dispatchPost({ type: 'refresh' });
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchPostId(i));
|
||||
}
|
||||
const postIds = await Promise.all(promises);
|
||||
promises = [];
|
||||
postIds.forEach((postId) => {
|
||||
promises.push(fetchPost(postId));
|
||||
});
|
||||
}, [DAORef, dispatchPost, fetchPost, fetchPostId]);
|
||||
|
||||
const fetchValidationPool = useCallback(async (poolIndex) => {
|
||||
const getPoolStatus = (pool) => {
|
||||
if (pool.resolved) {
|
||||
return pool.outcome ? 'Accepted' : 'Rejected';
|
||||
}
|
||||
return pool.timeRemaining > 0 ? 'In Progress' : 'Ready to Evaluate';
|
||||
};
|
||||
const pool = await DAORef.current.methods.validationPools(poolIndex).call();
|
||||
pool.id = Number(pool.id);
|
||||
pool.timeRemaining = new Date(Number(pool.endTime) * 1000) - new Date();
|
||||
pool.status = getPoolStatus(pool);
|
||||
dispatchValidationPool({ type: 'update', item: pool });
|
||||
|
||||
// When remaing time expires, we want to update the status for this pool
|
||||
if (pool.timeRemaining > 0) {
|
||||
setTimeout(() => {
|
||||
pool.timeRemaining = 0;
|
||||
pool.status = getPoolStatus(pool);
|
||||
dispatchValidationPool({ type: 'update', item: pool });
|
||||
}, pool.timeRemaining);
|
||||
}
|
||||
}, [DAORef, dispatchValidationPool]);
|
||||
|
||||
const fetchValidationPools = useCallback(async () => {
|
||||
// TODO: Pagination
|
||||
// TODO: Memoization
|
||||
// TODO: Caching
|
||||
const count = await DAORef.current.methods.validationPoolCount().call();
|
||||
const promises = [];
|
||||
dispatchValidationPool({ type: 'refresh' });
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchValidationPool(i));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}, [DAORef, dispatchValidationPool, fetchValidationPool]);
|
||||
|
||||
const fetchMember = useCallback(async (memberIndex) => {
|
||||
const id = await DAORef.current.methods.members(memberIndex).call();
|
||||
const member = { id };
|
||||
member.reputation = await DAORef.current.methods.balanceOf(id).call();
|
||||
dispatchMember({ type: 'updateById', item: member });
|
||||
return member;
|
||||
}, [DAORef, dispatchMember]);
|
||||
|
||||
const fetchMembers = useCallback(async () => {
|
||||
const count = await DAORef.current.methods.memberCount().call();
|
||||
const promises = [];
|
||||
dispatchMember({ type: 'refresh' });
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchMember(i));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}, [DAORef, dispatchMember, fetchMember]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- END FETCHERS --------------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
// In this effect, we initialize everything and add contract event listeners.
|
||||
useEffect(() => {
|
||||
if (!provider || !chainId || !account || balance === undefined) return () => {};
|
||||
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);
|
||||
const OnboardingContract = new web3.eth.Contract(OnboardingArtifact.abi, OnboardingAddress);
|
||||
DAORef.current = DAOContract;
|
||||
workRef.current = Work1Contract;
|
||||
onboardingRef.current = OnboardingContract;
|
||||
|
||||
fetchReputation();
|
||||
fetchMembers();
|
||||
fetchPosts();
|
||||
fetchValidationPools();
|
||||
|
||||
setDAO(DAOContract);
|
||||
setWork1(Work1Contract);
|
||||
setOnboarding(OnboardingContract);
|
||||
|
||||
// const fetchReputationInterval = setInterval(() => {
|
||||
// // console.log('reputation', reputation);
|
||||
// if (reputation !== undefined) {
|
||||
// clearInterval(fetchReputationInterval);
|
||||
// return;
|
||||
// }
|
||||
// fetchReputation();
|
||||
// }, 1000);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- BEGIN EVENT HANDLERS ------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
DAOContract.events.PostAdded({ fromBlock: 'latest' }).on('data', (event) => {
|
||||
console.log('event: post added');
|
||||
fetchPost(event.returnValues.id);
|
||||
});
|
||||
|
||||
DAOContract.events.ValidationPoolInitiated({ fromBlock: 'latest' }).on('data', (event) => {
|
||||
console.log('event: validation pool initiated');
|
||||
fetchValidationPool(event.returnValues.poolIndex);
|
||||
});
|
||||
|
||||
DAOContract.events.ValidationPoolResolved({ fromBlock: 'latest' }).on('data', (event) => {
|
||||
console.log('event: validation pool resolved');
|
||||
fetchReputation();
|
||||
fetchValidationPool(event.returnValues.poolIndex);
|
||||
fetchMembers();
|
||||
});
|
||||
|
||||
Work1Contract.events.AvailabilityStaked({ fromBlock: 'latest' }).on('data', () => {
|
||||
fetchReputation();
|
||||
});
|
||||
|
||||
OnboardingContract.events.AvailabilityStaked({ fromBlock: 'latest' }).on('data', () => {
|
||||
fetchReputation();
|
||||
});
|
||||
|
||||
return () => {
|
||||
DAOContract.events.PostAdded().off();
|
||||
DAOContract.events.ValidationPoolInitiated().off();
|
||||
DAOContract.events.ValidationPoolResolved().off();
|
||||
Work1Contract.events.AvailabilityStaked().off();
|
||||
OnboardingContract.events.AvailabilityStaked().off();
|
||||
};
|
||||
}, [provider, account, chainId, balance, dispatchValidationPool, dispatchPost,
|
||||
DAORef, workRef, onboardingRef,
|
||||
fetchPost, fetchPosts, fetchReputation, fetchValidationPool, fetchValidationPools, fetchMembers,
|
||||
]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- END MAIN INITIALIZION EFFECT ----------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
useEffect(() => {
|
||||
const { searchParams } = new URL(window.location.href);
|
||||
|
@ -286,56 +63,6 @@ function Widget() {
|
|||
}
|
||||
}, [sdk]);
|
||||
|
||||
const initiateValidationPool = useCallback(async (postId, poolDuration) => {
|
||||
const web3 = new Web3(provider);
|
||||
await DAO.methods.initiateValidationPool(
|
||||
postId,
|
||||
poolDuration ?? 3600,
|
||||
[1, 3],
|
||||
[1, 2],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
web3.eth.abi.encodeParameter('bytes', '0x00'),
|
||||
).send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
value: 10000,
|
||||
});
|
||||
}, [provider, DAO, account]);
|
||||
|
||||
const stake = useCallback(async (poolIndex, amount, inFavor) => {
|
||||
console.log(`Attempting to stake ${amount} ${inFavor ? 'for' : 'against'} pool ${poolIndex}`);
|
||||
await DAO.methods.stakeOnValidationPool(poolIndex, amount, inFavor).send({
|
||||
from: account,
|
||||
gas: 999999,
|
||||
});
|
||||
|
||||
// Since this is the result we expect from the server, we preemptively set it here.
|
||||
// We can let this value be negative -- this would just mean we'll be getting
|
||||
// at least one error from the server, and a corrected reputation.
|
||||
setReputation((current) => current - BigInt(amount));
|
||||
}, [DAO, account, setReputation]);
|
||||
|
||||
const stakeHalfInFavor = useCallback(async (poolIndex) => {
|
||||
await stake(poolIndex, reputation / BigInt(2), true);
|
||||
}, [stake, reputation]);
|
||||
|
||||
const evaluateOutcome = useCallback(async (poolIndex) => {
|
||||
await DAO.methods.evaluateOutcome(poolIndex).send({
|
||||
from: account,
|
||||
gas: 10000000,
|
||||
});
|
||||
}, [DAO, account]);
|
||||
|
||||
const handleShowAddPost = () => setShowAddPost(true);
|
||||
|
||||
const handleShowViewPost = useCallback(async ({ id }) => {
|
||||
const post = await Post.read(id);
|
||||
setViewPost(post);
|
||||
setShowViewPost(true);
|
||||
}, [setViewPost, setShowViewPost]);
|
||||
|
||||
// Sign and send a message
|
||||
const registerMatrixIdentity = useCallback(async () => {
|
||||
const message = new Date().toISOString();
|
||||
|
@ -356,12 +83,7 @@ function Widget() {
|
|||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
return (
|
||||
<Web3Context.Provider value={web3ProviderValue}>
|
||||
|
||||
<AddPostModal show={showAddPost} setShow={setShowAddPost} postToBlockchain />
|
||||
|
||||
<ViewPostModal show={showViewPost} setShow={setShowViewPost} post={viewPost} />
|
||||
|
||||
<>
|
||||
{!connected && <Button onClick={() => connect()}>Connect</Button>}
|
||||
|
||||
{connected && (
|
||||
|
@ -409,210 +131,11 @@ function Widget() {
|
|||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
<Tabs>
|
||||
<Tab eventKey="admin" title="Admin">
|
||||
<h2>Members</h2>
|
||||
<div>
|
||||
{`Members count: ${members.length}`}
|
||||
</div>
|
||||
<div>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Reputation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{members.filter((x) => !!x).map((member) => (
|
||||
<tr key={member.id}>
|
||||
<td>{member.id}</td>
|
||||
<td>{member.reputation.toString()}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{' '}
|
||||
<h2>Posts</h2>
|
||||
<div>
|
||||
<Button onClick={handleShowAddPost}>Add Post</Button>
|
||||
</div>
|
||||
<div>
|
||||
{`Posts count: ${posts.length}`}
|
||||
</div>
|
||||
<div>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Authors</th>
|
||||
<th>Sender</th>
|
||||
<th>Reputation</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{posts.filter((x) => !!x).map((post) => (
|
||||
<tr key={post.id}>
|
||||
<td>{post.id.toString()}</td>
|
||||
<td>
|
||||
<Stack>
|
||||
{post.authors.map(({ authorAddress, weightPPM }) => (
|
||||
<div key={authorAddress}>
|
||||
{getAddressName(chainId, authorAddress)}
|
||||
{' '}
|
||||
{Number(weightPPM) / 10000}
|
||||
%
|
||||
</div>
|
||||
))}
|
||||
</Stack>
|
||||
</td>
|
||||
<td>{getAddressName(chainId, post.sender)}</td>
|
||||
<td>{post.reputation.toString()}</td>
|
||||
<td>
|
||||
<Button onClick={() => handleShowViewPost(post)}>
|
||||
View Post
|
||||
</Button>
|
||||
{' '}
|
||||
Initiate Validation Pool
|
||||
{' '}
|
||||
<Button onClick={() => initiateValidationPool(post.id, 1)}>
|
||||
1s
|
||||
</Button>
|
||||
{' '}
|
||||
<Button onClick={() => initiateValidationPool(post.id, 20)}>
|
||||
20s
|
||||
</Button>
|
||||
{' '}
|
||||
<Button onClick={() => initiateValidationPool(post.id, 60)}>
|
||||
60s
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h2>Validation Pools</h2>
|
||||
<div>
|
||||
{`Validation Pool Count: ${validationPools.length}`}
|
||||
</div>
|
||||
<div>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Post ID</th>
|
||||
<th>Sender</th>
|
||||
<th>Fee</th>
|
||||
<th>Binding</th>
|
||||
<th>Quorum</th>
|
||||
<th>WinRatio</th>
|
||||
<th>
|
||||
Redistribute
|
||||
<br />
|
||||
Losing Stakes
|
||||
</th>
|
||||
<th>Duration</th>
|
||||
<th>End Time</th>
|
||||
<th>
|
||||
Stake
|
||||
<br />
|
||||
Count
|
||||
</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{validationPools.filter((x) => !!x).map((pool) => (
|
||||
<tr key={pool.id}>
|
||||
<td>{pool.id.toString()}</td>
|
||||
<td>{pool.postId}</td>
|
||||
<td>{getAddressName(chainId, pool.sender)}</td>
|
||||
<td>{pool.fee.toString()}</td>
|
||||
<td>
|
||||
{pool.params.bindingPercent.toString()}
|
||||
%
|
||||
</td>
|
||||
<td>{`${pool.params.quorum[0].toString()}/${pool.params.quorum[1].toString()}`}</td>
|
||||
<td>{`${pool.params.winRatio[0].toString()}/${pool.params.winRatio[1].toString()}`}</td>
|
||||
<td>{pool.params.redistributeLosingStakes.toString()}</td>
|
||||
<td>{pool.params.duration.toString()}</td>
|
||||
<td>{new Date(Number(pool.endTime) * 1000).toLocaleString()}</td>
|
||||
<td>{pool.stakeCount.toString()}</td>
|
||||
<td>{pool.status}</td>
|
||||
<td>
|
||||
{!pool.resolved && reputation > 0 && pool.timeRemaining > 0 && (
|
||||
<>
|
||||
<Button onClick={() => stakeHalfInFavor(pool.id)}>
|
||||
Stake 1/2 REP
|
||||
</Button>
|
||||
{' '}
|
||||
<Button onClick={() => stake(pool.id, reputation, true)}>
|
||||
Stake All
|
||||
</Button>
|
||||
{' '}
|
||||
</>
|
||||
)}
|
||||
{!pool.resolved && (pool.timeRemaining <= 0 || !reputation) && (
|
||||
<Button onClick={() => evaluateOutcome(pool.id)}>
|
||||
Evaluate Outcome
|
||||
</Button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab eventKey="worker" title="Worker">
|
||||
{work1 && (
|
||||
<WorkContract
|
||||
workContract={work1}
|
||||
title="Work Contract 1"
|
||||
verb="Work"
|
||||
showProposePriceChange
|
||||
/>
|
||||
)}
|
||||
{onboarding && (
|
||||
<WorkContract
|
||||
workContract={onboarding}
|
||||
title="Onboarding"
|
||||
verb="Onboarding"
|
||||
showRequestWork
|
||||
showProposePriceChange
|
||||
/>
|
||||
)}
|
||||
</Tab>
|
||||
<Tab eventKey="customer" title="Customer">
|
||||
{work1 && (
|
||||
<WorkContract
|
||||
workContract={work1}
|
||||
showAvailabilityActions={false}
|
||||
showAvailabilityAmount={false}
|
||||
onlyShowAvailable
|
||||
title="Work Contract 1"
|
||||
verb="Work"
|
||||
showRequestWork
|
||||
/>
|
||||
)}
|
||||
</Tab>
|
||||
<Tab eventKey="proposals" title="Proposals">
|
||||
<Proposals />
|
||||
</Tab>
|
||||
<Tab eventKey="import" title="Import">
|
||||
<h1>Semantic Scholar Import</h1>
|
||||
<ImportPaper />
|
||||
<ImportPapersByAuthor />
|
||||
<ImportMatrixEvent />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<MainTabs />
|
||||
</>
|
||||
)}
|
||||
</Web3Context.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,10 @@ function Proposals() {
|
|||
[{ authorAddress: account, weightPPM: 1000000 }],
|
||||
post.hash,
|
||||
[], // TODO: Proposal can cite posts from matrix, semantic scholar, etc
|
||||
);
|
||||
).send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
});
|
||||
// Now submit the post as a proposal
|
||||
await proposalsContract.current.methods.propose(
|
||||
post.hash,
|
||||
|
|
|
@ -11,7 +11,7 @@ function AddPostModal({
|
|||
show, setShow, title, postToBlockchain, onSubmit,
|
||||
}) {
|
||||
const {
|
||||
provider, DAO, account,
|
||||
provider, DAORef, account,
|
||||
} = useContext(Web3Context);
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
|
@ -29,13 +29,13 @@ function AddPostModal({
|
|||
await post.write();
|
||||
// If requested, upload the hash to the blockchain
|
||||
if (postToBlockchain) {
|
||||
await post.publish(DAO, account);
|
||||
await post.publish(DAORef.current, account);
|
||||
}
|
||||
// If requested, call callback
|
||||
if (onSubmit) {
|
||||
onSubmit(post);
|
||||
}
|
||||
}, [provider, DAO, account, content, setShow, postToBlockchain, onSubmit]);
|
||||
}, [provider, DAORef, account, content, setShow, postToBlockchain, onSubmit]);
|
||||
|
||||
return (
|
||||
<Modal className="modal" show={show} onHide={handleClose}>
|
||||
|
|
|
@ -0,0 +1,308 @@
|
|||
import {
|
||||
useCallback, useState,
|
||||
} from 'react'; import Button from 'react-bootstrap/Button';
|
||||
import { Web3 } from 'web3';
|
||||
import Tab from 'react-bootstrap/Tab';
|
||||
import Tabs from 'react-bootstrap/Tabs';
|
||||
import Stack from 'react-bootstrap/Stack';
|
||||
|
||||
import WorkContract from '../work-contracts/WorkContract';
|
||||
import Proposals from '../Proposals';
|
||||
import ImportPaper from '../ImportPaper';
|
||||
import ImportPapersByAuthor from '../ImportPapersByAuthor';
|
||||
import getAddressName from '../../utils/get-address-name';
|
||||
import ImportMatrixEvent from '../ImportMatrixEvent';
|
||||
import AddPostModal from '../posts/AddPostModal';
|
||||
import ViewPostModal from '../posts/ViewPostModal';
|
||||
import Post from '../../utils/Post';
|
||||
import useMainContext from '../../contexts/useMainContext';
|
||||
|
||||
function MainTabs() {
|
||||
const {
|
||||
provider, chainId, account, members, reputation, setReputation, posts, validationPools,
|
||||
DAORef, work1, work2, onboarding,
|
||||
} = useMainContext();
|
||||
|
||||
const [showAddPost, setShowAddPost] = useState(false);
|
||||
const [showViewPost, setShowViewPost] = useState(false);
|
||||
const [viewPost, setViewPost] = useState({});
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- BEGIN UI ACTIONS ----------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
const initiateValidationPool = useCallback(async (postId, poolDuration) => {
|
||||
const web3 = new Web3(provider);
|
||||
await DAORef.current.methods.initiateValidationPool(
|
||||
postId,
|
||||
poolDuration ?? 3600,
|
||||
[1, 3],
|
||||
[1, 2],
|
||||
100,
|
||||
true,
|
||||
false,
|
||||
web3.eth.abi.encodeParameter('bytes', '0x00'),
|
||||
).send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
value: 10000,
|
||||
});
|
||||
}, [provider, DAORef, account]);
|
||||
|
||||
const stake = useCallback(async (poolIndex, amount, inFavor) => {
|
||||
console.log(`Attempting to stake ${amount} ${inFavor ? 'for' : 'against'} pool ${poolIndex}`);
|
||||
await DAORef.current.methods.stakeOnValidationPool(poolIndex, amount, inFavor).send({
|
||||
from: account,
|
||||
gas: 999999,
|
||||
});
|
||||
|
||||
// Since this is the result we expect from the server, we preemptively set it here.
|
||||
// We can let this value be negative -- this would just mean we'll be getting
|
||||
// at least one error from the server, and a corrected reputation.
|
||||
setReputation((current) => current - BigInt(amount));
|
||||
}, [DAORef, account, setReputation]);
|
||||
|
||||
const stakeHalfInFavor = useCallback(async (poolIndex) => {
|
||||
await stake(poolIndex, reputation / BigInt(2), true);
|
||||
}, [stake, reputation]);
|
||||
|
||||
const evaluateOutcome = useCallback(async (poolIndex) => {
|
||||
await DAORef.current.methods.evaluateOutcome(poolIndex).send({
|
||||
from: account,
|
||||
gas: 10000000,
|
||||
});
|
||||
}, [DAORef, account]);
|
||||
|
||||
const handleShowAddPost = () => setShowAddPost(true);
|
||||
|
||||
const handleShowViewPost = useCallback(async ({ id }) => {
|
||||
const post = await Post.read(id);
|
||||
setViewPost(post);
|
||||
setShowViewPost(true);
|
||||
}, [setViewPost, setShowViewPost]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- END UI ACTIONS ------------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<AddPostModal show={showAddPost} setShow={setShowAddPost} postToBlockchain />
|
||||
|
||||
<ViewPostModal show={showViewPost} setShow={setShowViewPost} post={viewPost} />
|
||||
<Tabs>
|
||||
<Tab eventKey="admin" title="Admin">
|
||||
<h2>Members</h2>
|
||||
<div>
|
||||
{`Members count: ${members.length}`}
|
||||
</div>
|
||||
<div>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Reputation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{members.filter((x) => !!x).map((member) => (
|
||||
<tr key={member.id}>
|
||||
<td>{member.id}</td>
|
||||
<td>{member.reputation.toString()}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{' '}
|
||||
<h2>Posts</h2>
|
||||
<div>
|
||||
<Button onClick={handleShowAddPost}>Add Post</Button>
|
||||
</div>
|
||||
<div>
|
||||
{`Posts count: ${posts.length}`}
|
||||
</div>
|
||||
<div>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Authors</th>
|
||||
<th>Sender</th>
|
||||
<th>Reputation</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{posts.filter((x) => !!x).map((post) => (
|
||||
<tr key={post.id}>
|
||||
<td>{post.id.toString()}</td>
|
||||
<td>
|
||||
<Stack>
|
||||
{post.authors.map(({ authorAddress, weightPPM }) => (
|
||||
<div key={authorAddress}>
|
||||
{getAddressName(chainId, authorAddress)}
|
||||
{' '}
|
||||
{Number(weightPPM) / 10000}
|
||||
%
|
||||
</div>
|
||||
))}
|
||||
</Stack>
|
||||
</td>
|
||||
<td>{getAddressName(chainId, post.sender)}</td>
|
||||
<td>{post.reputation.toString()}</td>
|
||||
<td>
|
||||
<Button onClick={() => handleShowViewPost(post)}>
|
||||
View Post
|
||||
</Button>
|
||||
{' '}
|
||||
Initiate Validation Pool
|
||||
{' '}
|
||||
<Button onClick={() => initiateValidationPool(post.id, 1)}>
|
||||
1s
|
||||
</Button>
|
||||
{' '}
|
||||
<Button onClick={() => initiateValidationPool(post.id, 20)}>
|
||||
20s
|
||||
</Button>
|
||||
{' '}
|
||||
<Button onClick={() => initiateValidationPool(post.id, 60)}>
|
||||
60s
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h2>Validation Pools</h2>
|
||||
<div>
|
||||
{`Validation Pool Count: ${validationPools.length}`}
|
||||
</div>
|
||||
<div>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Post ID</th>
|
||||
<th>Sender</th>
|
||||
<th>Fee</th>
|
||||
<th>Binding</th>
|
||||
<th>Quorum</th>
|
||||
<th>WinRatio</th>
|
||||
<th>
|
||||
Redistribute
|
||||
<br />
|
||||
Losing Stakes
|
||||
</th>
|
||||
<th>Duration</th>
|
||||
<th>End Time</th>
|
||||
<th>
|
||||
Stake
|
||||
<br />
|
||||
Count
|
||||
</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{validationPools.filter((x) => !!x).map((pool) => (
|
||||
<tr key={pool.id}>
|
||||
<td>{pool.id.toString()}</td>
|
||||
<td>{pool.postId}</td>
|
||||
<td>{getAddressName(chainId, pool.sender)}</td>
|
||||
<td>{pool.fee.toString()}</td>
|
||||
<td>
|
||||
{pool.params.bindingPercent.toString()}
|
||||
%
|
||||
</td>
|
||||
<td>{`${pool.params.quorum[0].toString()}/${pool.params.quorum[1].toString()}`}</td>
|
||||
<td>{`${pool.params.winRatio[0].toString()}/${pool.params.winRatio[1].toString()}`}</td>
|
||||
<td>{pool.params.redistributeLosingStakes.toString()}</td>
|
||||
<td>{pool.params.duration.toString()}</td>
|
||||
<td>{new Date(Number(pool.endTime) * 1000).toLocaleString()}</td>
|
||||
<td>{pool.stakeCount.toString()}</td>
|
||||
<td>{pool.status}</td>
|
||||
<td>
|
||||
{!pool.resolved && reputation > 0 && pool.timeRemaining > 0 && (
|
||||
<>
|
||||
<Button onClick={() => stakeHalfInFavor(pool.id)}>
|
||||
Stake 1/2 REP
|
||||
</Button>
|
||||
{' '}
|
||||
<Button onClick={() => stake(pool.id, reputation, true)}>
|
||||
Stake All
|
||||
</Button>
|
||||
{' '}
|
||||
</>
|
||||
)}
|
||||
{!pool.resolved && (pool.timeRemaining <= 0 || !reputation) && (
|
||||
<Button onClick={() => evaluateOutcome(pool.id)}>
|
||||
Evaluate Outcome
|
||||
</Button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab eventKey="worker" title="Worker">
|
||||
{work1 && (
|
||||
<WorkContract
|
||||
workContract={work1}
|
||||
title="Work Contract 1"
|
||||
verb="Work"
|
||||
showProposePriceChange
|
||||
/>
|
||||
)}
|
||||
{onboarding && (
|
||||
<WorkContract
|
||||
workContract={onboarding}
|
||||
title="Onboarding"
|
||||
verb="Onboarding"
|
||||
showRequestWork
|
||||
showProposePriceChange
|
||||
/>
|
||||
)}
|
||||
{work2 && (
|
||||
<WorkContract
|
||||
workContract={work2}
|
||||
title="Work Contract 2"
|
||||
verb="Work"
|
||||
showProposePriceChange
|
||||
/>
|
||||
)}
|
||||
</Tab>
|
||||
<Tab eventKey="customer" title="Customer">
|
||||
{work1 && (
|
||||
<WorkContract
|
||||
workContract={work1}
|
||||
showAvailabilityActions={false}
|
||||
showAvailabilityAmount={false}
|
||||
onlyShowAvailable
|
||||
title="Work Contract 1"
|
||||
verb="Work"
|
||||
showRequestWork
|
||||
/>
|
||||
)}
|
||||
</Tab>
|
||||
<Tab eventKey="proposals" title="Proposals">
|
||||
<Proposals />
|
||||
</Tab>
|
||||
<Tab eventKey="import" title="Import">
|
||||
<h1>Semantic Scholar</h1>
|
||||
<ImportPaper />
|
||||
<ImportPapersByAuthor />
|
||||
<h1>Matrix</h1>
|
||||
<ImportMatrixEvent />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default MainTabs;
|
|
@ -0,0 +1,238 @@
|
|||
import {
|
||||
useCallback, useEffect, useState, useMemo, useRef,
|
||||
} from 'react';
|
||||
import { useSDK } from '@metamask/sdk-react';
|
||||
import { Web3 } from 'web3';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Web3Context from './Web3Context';
|
||||
import useList from '../utils/List';
|
||||
import { getContractAddressByChainId } from '../utils/contract-config';
|
||||
import DAOArtifact from '../../contractArtifacts/DAO.json';
|
||||
import Work1Artifact from '../../contractArtifacts/Work1.json';
|
||||
import OnboardingArtifact from '../../contractArtifacts/Onboarding.json';
|
||||
import Work2Artifact from '../../contractArtifacts/Work2.json';
|
||||
|
||||
function MainContextProvider({ children }) {
|
||||
const {
|
||||
sdk, connected, provider, chainId, account, balance,
|
||||
} = useSDK();
|
||||
const [DAO, setDAO] = useState();
|
||||
const [work1, setWork1] = useState();
|
||||
const [work2, setWork2] = useState();
|
||||
const [onboarding, setOnboarding] = useState();
|
||||
const DAORef = useRef();
|
||||
const [balanceEther, setBalanceEther] = useState();
|
||||
const [reputation, setReputation] = useState();
|
||||
const [totalReputation, setTotalReputation] = useState();
|
||||
const [posts, dispatchPost] = useList();
|
||||
const [validationPools, dispatchValidationPool] = useList();
|
||||
const [members, dispatchMember] = useList();
|
||||
const web3ProviderValue = useMemo(() => ({
|
||||
provider,
|
||||
reputation,
|
||||
setReputation,
|
||||
totalReputation,
|
||||
account,
|
||||
chainId,
|
||||
posts,
|
||||
DAO,
|
||||
DAORef,
|
||||
work1,
|
||||
work2,
|
||||
onboarding,
|
||||
members,
|
||||
validationPools,
|
||||
balanceEther,
|
||||
sdk,
|
||||
connected,
|
||||
}), [
|
||||
provider, reputation, setReputation, account, chainId, posts, balanceEther, totalReputation,
|
||||
DAO, DAORef, work1, work2, onboarding, members, validationPools, sdk, connected]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!provider || balance === undefined) return;
|
||||
const web3 = new Web3(provider);
|
||||
setBalanceEther(web3.utils.fromWei(balance, 'ether'));
|
||||
}, [provider, balance]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- BEGIN FETCHERS ------------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
const fetchReputation = useCallback(async () => {
|
||||
setReputation(await DAORef.current.methods.balanceOf(account).call());
|
||||
setTotalReputation(await DAORef.current.methods.totalSupply().call());
|
||||
}, [DAORef, account]);
|
||||
|
||||
const fetchPost = useCallback(async (postId) => {
|
||||
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) => {
|
||||
const postId = await DAORef.current.methods.postIds(postIndex).call();
|
||||
return postId;
|
||||
}, [DAORef]);
|
||||
|
||||
const fetchPosts = useCallback(async () => {
|
||||
const count = await DAORef.current.methods.postCount().call();
|
||||
let promises = [];
|
||||
dispatchPost({ type: 'refresh' });
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchPostId(i));
|
||||
}
|
||||
const postIds = await Promise.all(promises);
|
||||
promises = [];
|
||||
postIds.forEach((postId) => {
|
||||
promises.push(fetchPost(postId));
|
||||
});
|
||||
}, [DAORef, dispatchPost, fetchPost, fetchPostId]);
|
||||
|
||||
const fetchValidationPool = useCallback(async (poolIndex) => {
|
||||
const getPoolStatus = (pool) => {
|
||||
if (pool.resolved) {
|
||||
return pool.outcome ? 'Accepted' : 'Rejected';
|
||||
}
|
||||
return pool.timeRemaining > 0 ? 'In Progress' : 'Ready to Evaluate';
|
||||
};
|
||||
const pool = await DAORef.current.methods.validationPools(poolIndex).call();
|
||||
pool.id = Number(pool.id);
|
||||
pool.timeRemaining = new Date(Number(pool.endTime) * 1000) - new Date();
|
||||
pool.status = getPoolStatus(pool);
|
||||
dispatchValidationPool({ type: 'update', item: pool });
|
||||
|
||||
// When remaing time expires, we want to update the status for this pool
|
||||
if (pool.timeRemaining > 0) {
|
||||
setTimeout(() => {
|
||||
pool.timeRemaining = 0;
|
||||
pool.status = getPoolStatus(pool);
|
||||
dispatchValidationPool({ type: 'update', item: pool });
|
||||
}, pool.timeRemaining);
|
||||
}
|
||||
}, [DAORef, dispatchValidationPool]);
|
||||
|
||||
const fetchValidationPools = useCallback(async () => {
|
||||
// TODO: Pagination
|
||||
// TODO: Memoization
|
||||
// TODO: Caching
|
||||
const count = await DAORef.current.methods.validationPoolCount().call();
|
||||
const promises = [];
|
||||
dispatchValidationPool({ type: 'refresh' });
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchValidationPool(i));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}, [DAORef, dispatchValidationPool, fetchValidationPool]);
|
||||
|
||||
const fetchMember = useCallback(async (memberIndex) => {
|
||||
const id = await DAORef.current.methods.members(memberIndex).call();
|
||||
const member = { id };
|
||||
member.reputation = await DAORef.current.methods.balanceOf(id).call();
|
||||
dispatchMember({ type: 'updateById', item: member });
|
||||
return member;
|
||||
}, [DAORef, dispatchMember]);
|
||||
|
||||
const fetchMembers = useCallback(async () => {
|
||||
const count = await DAORef.current.methods.memberCount().call();
|
||||
const promises = [];
|
||||
dispatchMember({ type: 'refresh' });
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchMember(i));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}, [DAORef, dispatchMember, fetchMember]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- END FETCHERS --------------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
// In this effect, we initialize everything and add contract event listeners.
|
||||
useEffect(() => {
|
||||
if (!provider || !chainId || !account || balance === undefined) return () => {};
|
||||
const DAOAddress = getContractAddressByChainId(chainId, 'DAO');
|
||||
const Work1Address = getContractAddressByChainId(chainId, 'Work1');
|
||||
const OnboardingAddress = getContractAddressByChainId(chainId, 'Onboarding');
|
||||
const Work2Address = getContractAddressByChainId(chainId, 'Work2');
|
||||
const web3 = new Web3(provider);
|
||||
DAORef.current = new web3.eth.Contract(DAOArtifact.abi, DAOAddress);
|
||||
setDAO(DAORef.current);
|
||||
const work1Contract = new web3.eth.Contract(Work1Artifact.abi, Work1Address);
|
||||
setWork1(work1Contract);
|
||||
const onboardingContract = new web3.eth.Contract(OnboardingArtifact.abi, OnboardingAddress);
|
||||
setOnboarding(onboardingContract);
|
||||
const work2Contract = new web3.eth.Contract(Work2Artifact.abi, Work2Address);
|
||||
setWork2(work2Contract);
|
||||
|
||||
fetchReputation();
|
||||
fetchMembers();
|
||||
fetchPosts();
|
||||
fetchValidationPools();
|
||||
|
||||
// const fetchReputationInterval = setInterval(() => {
|
||||
// // console.log('reputation', reputation);
|
||||
// if (reputation !== undefined) {
|
||||
// clearInterval(fetchReputationInterval);
|
||||
// return;
|
||||
// }
|
||||
// fetchReputation();
|
||||
// }, 1000);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- BEGIN EVENT HANDLERS ------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
DAORef.current.events.PostAdded({ fromBlock: 'latest' }).on('data', (event) => {
|
||||
console.log('event: post added');
|
||||
fetchPost(event.returnValues.id);
|
||||
});
|
||||
|
||||
DAORef.current.events.ValidationPoolInitiated({ fromBlock: 'latest' }).on('data', (event) => {
|
||||
console.log('event: validation pool initiated');
|
||||
fetchValidationPool(event.returnValues.poolIndex);
|
||||
});
|
||||
|
||||
DAORef.current.events.ValidationPoolResolved({ fromBlock: 'latest' }).on('data', (event) => {
|
||||
console.log('event: validation pool resolved');
|
||||
fetchReputation();
|
||||
fetchValidationPool(event.returnValues.poolIndex);
|
||||
fetchMembers();
|
||||
});
|
||||
|
||||
work1Contract.events.AvailabilityStaked({ fromBlock: 'latest' }).on('data', () => {
|
||||
fetchReputation();
|
||||
});
|
||||
|
||||
onboardingContract.events.AvailabilityStaked({ fromBlock: 'latest' }).on('data', () => {
|
||||
fetchReputation();
|
||||
});
|
||||
|
||||
return () => {
|
||||
DAORef.current.events.PostAdded().off();
|
||||
DAORef.current.events.ValidationPoolInitiated().off();
|
||||
DAORef.current.events.ValidationPoolResolved().off();
|
||||
work1Contract.events.AvailabilityStaked().off();
|
||||
onboardingContract.events.AvailabilityStaked().off();
|
||||
};
|
||||
}, [provider, account, chainId, balance, dispatchValidationPool, dispatchPost,
|
||||
DAORef, setDAO, setWork1, setOnboarding, setWork2,
|
||||
fetchPost, fetchPosts, fetchReputation, fetchValidationPool, fetchValidationPools, fetchMembers,
|
||||
]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- END MAIN INITIALIZION EFFECT ----------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
return (
|
||||
<Web3Context.Provider value={web3ProviderValue}>
|
||||
{children}
|
||||
</Web3Context.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
MainContextProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default MainContextProvider;
|
|
@ -0,0 +1,9 @@
|
|||
import { useContext } from 'react';
|
||||
|
||||
import Web3Context from './Web3Context';
|
||||
|
||||
function useMainContext() {
|
||||
return useContext(Web3Context);
|
||||
}
|
||||
|
||||
export default useMainContext;
|
|
@ -10,6 +10,7 @@ import {
|
|||
|
||||
import WebApp from './WebApp';
|
||||
import Widget from './Widget';
|
||||
import MainContextProvider from './contexts/MainContextProvider';
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
|
@ -37,7 +38,9 @@ ReactDOM.createRoot(document.getElementById('root')).render(
|
|||
},
|
||||
}}
|
||||
>
|
||||
<RouterProvider router={router} />
|
||||
<MainContextProvider>
|
||||
<RouterProvider router={router} />
|
||||
</MainContextProvider>
|
||||
</MetaMaskProvider>
|
||||
</React.StrictMode>,
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue