Add callback for proposals
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 28s
Details
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 28s
Details
This commit is contained in:
parent
2806eff0bb
commit
18693ac474
|
@ -1,5 +1,5 @@
|
|||
import {
|
||||
useCallback, useEffect, useState, useMemo,
|
||||
useCallback, useEffect, useState, useMemo, useRef,
|
||||
} from 'react';
|
||||
import { useSDK } from '@metamask/sdk-react';
|
||||
import { Web3 } from 'web3';
|
||||
|
@ -31,6 +31,9 @@ function App() {
|
|||
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();
|
||||
|
@ -54,9 +57,84 @@ function App() {
|
|||
account,
|
||||
chainId,
|
||||
posts,
|
||||
DAORef,
|
||||
workRef,
|
||||
onboardingRef,
|
||||
}), [
|
||||
provider, DAO, work1, onboarding, reputation, setReputation, account, chainId, posts]);
|
||||
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 (postIndex) => {
|
||||
const p = await DAORef.current.methods.posts(postIndex).call();
|
||||
p.id = Number(p.id);
|
||||
dispatchPost({ type: 'update', item: p });
|
||||
return p;
|
||||
}, [DAORef, dispatchPost]);
|
||||
|
||||
const fetchPosts = useCallback(async () => {
|
||||
const count = await DAORef.current.methods.postCount().call();
|
||||
const promises = [];
|
||||
dispatchPost({ type: 'refresh' });
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchPost(i));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}, [DAORef, dispatchPost, fetchPost]);
|
||||
|
||||
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]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- END FETCHERS --------------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
// In this effect, we initialize everything and add contract event listeners.
|
||||
useEffect(() => {
|
||||
if (!provider || !chainId || !account || balance === undefined) return () => {};
|
||||
|
@ -67,80 +145,26 @@ function App() {
|
|||
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();
|
||||
fetchPosts();
|
||||
fetchValidationPools();
|
||||
|
||||
setDAO(DAOContract);
|
||||
setWork1(Work1Contract);
|
||||
setOnboarding(OnboardingContract);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- BEGIN FETCHERS ------------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
const fetchReputation = async () => {
|
||||
setReputation(await DAOContract.methods.balanceOf(account).call());
|
||||
setTotalReputation(await DAOContract.methods.totalSupply().call());
|
||||
};
|
||||
|
||||
const fetchPost = async (postIndex) => {
|
||||
const p = await DAOContract.methods.posts(postIndex).call();
|
||||
p.id = Number(p.id);
|
||||
dispatchPost({ type: 'update', item: p });
|
||||
return p;
|
||||
};
|
||||
|
||||
const fetchPosts = async () => {
|
||||
const count = await DAOContract.methods.postCount().call();
|
||||
const promises = [];
|
||||
dispatchPost({ type: 'refresh' });
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchPost(i));
|
||||
const fetchReputationInterval = setInterval(() => {
|
||||
console.log('reputation', reputation);
|
||||
if (reputation !== undefined) {
|
||||
clearInterval(fetchReputationInterval);
|
||||
return;
|
||||
}
|
||||
await Promise.all(promises);
|
||||
};
|
||||
|
||||
const fetchValidationPool = 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 DAOContract.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);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchValidationPools = async () => {
|
||||
// TODO: Pagination
|
||||
// TODO: Memoization
|
||||
// TODO: Caching
|
||||
const count = await DAOContract.methods.validationPoolCount().call();
|
||||
const promises = [];
|
||||
dispatchValidationPool({ type: 'refresh' });
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchValidationPool(i));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- END FETCHERS --------------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
fetchReputation();
|
||||
fetchPosts();
|
||||
fetchValidationPools();
|
||||
fetchReputation();
|
||||
}, 1000);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- BEGIN EVENT HANDLERS ------------------------------- */
|
||||
|
@ -177,18 +201,15 @@ function App() {
|
|||
Work1Contract.events.AvailabilityStaked().off();
|
||||
OnboardingContract.events.AvailabilityStaked().off();
|
||||
};
|
||||
}, [provider, account, chainId, balance, dispatchValidationPool, dispatchPost]);
|
||||
}, [provider, account, chainId, balance, dispatchValidationPool, dispatchPost, reputation,
|
||||
DAORef, workRef, onboardingRef,
|
||||
fetchPost, fetchPosts, fetchReputation, fetchValidationPool, fetchValidationPools,
|
||||
]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- END MAIN INITIALIZION EFFECT ----------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
||||
useEffect(() => {
|
||||
if (!provider || balance === undefined) return;
|
||||
const web3 = new Web3(provider);
|
||||
setBalanceEther(web3.utils.fromWei(balance, 'ether'));
|
||||
}, [provider, balance]);
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- BEGIN UI ACTIONS ----------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
|
|
@ -102,18 +102,22 @@ function Proposals() {
|
|||
}, [posts, setViewPost, setShowViewProposal]);
|
||||
|
||||
const onSubmitProposal = useCallback(async (post) => {
|
||||
const web3 = new Web3(provider);
|
||||
const emptyCallbackData = web3.eth.abi.encodeParameter('bytes', '0x00');
|
||||
// TODO: Make referenda durations configurable
|
||||
await proposalsContract.current.methods.propose(
|
||||
post.hash,
|
||||
durations[0],
|
||||
durations[1],
|
||||
durations[2],
|
||||
false,
|
||||
emptyCallbackData,
|
||||
).send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
value: 10000,
|
||||
});
|
||||
}, [account, proposalsContract, durations]);
|
||||
}, [provider, account, proposalsContract, durations]);
|
||||
|
||||
const handleAttest = useCallback(async (proposalIndex) => {
|
||||
await proposalsContract.current.methods.attest(proposalIndex, reputation).send({
|
||||
|
|
|
@ -43,6 +43,8 @@ contract Proposals is DAOContract, IOnValidate {
|
|||
mapping(address => uint) attestations;
|
||||
uint attestationTotal;
|
||||
Referendum[3] referenda;
|
||||
bool callbackOnValidate;
|
||||
bytes callbackData;
|
||||
}
|
||||
|
||||
mapping(uint => Proposal) public proposals;
|
||||
|
@ -62,7 +64,9 @@ contract Proposals is DAOContract, IOnValidate {
|
|||
string calldata contentId,
|
||||
uint referendum0Duration,
|
||||
uint referendum1Duration,
|
||||
uint referendum100Duration
|
||||
uint referendum100Duration,
|
||||
bool callbackOnValidate,
|
||||
bytes calldata callbackData
|
||||
) external payable returns (uint proposalIndex) {
|
||||
uint postIndex = dao.addPost(msg.sender, contentId);
|
||||
proposalIndex = proposalCount++;
|
||||
|
@ -74,6 +78,8 @@ contract Proposals is DAOContract, IOnValidate {
|
|||
proposal.referenda[2].duration = referendum100Duration;
|
||||
proposal.fee = msg.value;
|
||||
proposal.remainingFee = proposal.fee;
|
||||
proposal.callbackOnValidate = callbackOnValidate;
|
||||
proposal.callbackData = callbackData;
|
||||
emit NewProposal(proposalIndex);
|
||||
}
|
||||
|
||||
|
@ -204,11 +210,32 @@ contract Proposals is DAOContract, IOnValidate {
|
|||
require(referendumIndex == 2, "Stage 2 index mismatch");
|
||||
// Note that no retries are attempted for referendum 100%
|
||||
if (votePasses && participationAboveThreshold) {
|
||||
// TODO: The proposal has passed all referenda and should become "law"
|
||||
// The proposal has passed all referenda and should become "law"
|
||||
proposal.stage = Stage.Accepted;
|
||||
// This is an opportunity for some actions to occur
|
||||
// We should at least emit an event
|
||||
proposal.stage = Stage.Accepted;
|
||||
emit ProposalAccepted(proposalIndex);
|
||||
// We also execute a callback, if requested
|
||||
if (proposal.callbackOnValidate) {
|
||||
try
|
||||
// Note: We're directly reusing the onValidate hook we established for valdiation pools.
|
||||
// if any contracts want to use both callbacks, distinct interfaces should be defined.
|
||||
IOnValidate(proposal.sender).onValidate(
|
||||
votePasses,
|
||||
false,
|
||||
stakedFor,
|
||||
stakedAgainst,
|
||||
proposal.callbackData
|
||||
)
|
||||
{
|
||||
console.log("proposal callbackOnValidate succeed");
|
||||
} catch Error(string memory reason) {
|
||||
console.log(
|
||||
"proposal callbackOnValidate failed:",
|
||||
reason
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (referendum.retryCount >= 2) {
|
||||
proposal.stage = Stage.Failed;
|
||||
emit ProposalFailed(proposalIndex, "Retry count exceeded");
|
||||
|
|
|
@ -78,7 +78,8 @@ describe('Proposal', () => {
|
|||
account2,
|
||||
} = await loadFixture(deploy));
|
||||
|
||||
await proposals.propose('proposal-content-id', 20, 20, 20, { value: 100 });
|
||||
const emptyCallbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
await proposals.propose('proposal-content-id', 20, 20, 20, false, emptyCallbackData, { value: 100 });
|
||||
expect(await proposals.proposalCount()).to.equal(1);
|
||||
proposal = await proposals.proposals(0);
|
||||
expect(proposal.postIndex).to.equal(2);
|
||||
|
|
Loading…
Reference in New Issue