automatic staking
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 33s
Details
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 33s
Details
This commit is contained in:
parent
5e981b11aa
commit
3da0382247
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useSDK } from '@metamask/sdk-react';
|
||||
import { Web3 } from 'web3';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
|
@ -8,8 +8,8 @@ import DAOArtifact from './assets/DAO.json';
|
|||
|
||||
const contracts = {
|
||||
'0x539': { // Hardhat
|
||||
DAO: '0x63472674239ffb70618Fae043610917f2d9B781C',
|
||||
Work1: '0xC62b0b16B3ef06c417BFC4Fb02E0Da06aF5A95Ef',
|
||||
DAO: '0x8d914D38dD301FC4606f5aa9fEcF8A76389020d3',
|
||||
Work1: '0x050C420Cc4995B41217Eba1B54B82Fd5687e9139',
|
||||
},
|
||||
'0xaa36a7': { // Sepolia
|
||||
DAO: '0x8F00038542C87A5eAf18d5938B7723bF2A04A4e4',
|
||||
|
@ -26,10 +26,12 @@ function App() {
|
|||
// const [work1, setWork1] = useState();
|
||||
// const [work1Price, setWork1Price] = useState();
|
||||
const [balanceEther, setBalanceEther] = useState();
|
||||
const [reputation, setReputation] = useState();
|
||||
// const [reputation, setReputation] = useState();
|
||||
const reputation = useRef();
|
||||
const [totalReputation, setTotalReputation] = useState();
|
||||
const [posts, setPosts] = useState([]);
|
||||
const [validationPools, setValidationPools] = useState([]);
|
||||
const stakedPools = useRef([]);
|
||||
|
||||
// const watchReputationToken = useCallback(async () => {
|
||||
// await provider.request({
|
||||
|
@ -43,6 +45,14 @@ function App() {
|
|||
// });
|
||||
// }, [provider]);
|
||||
|
||||
const getStatus = (pool) => {
|
||||
if (pool.resolved) {
|
||||
return pool.outcome ? 'Accepted' : 'Rejected';
|
||||
}
|
||||
const endDate = new Date(Number(pool.endTime) * 1000);
|
||||
return new Date() < endDate ? 'In Progress' : 'Ready to Evaluate';
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!provider || !chainId || !account) return;
|
||||
if (!contracts[chainId]) return;
|
||||
|
@ -51,12 +61,10 @@ function App() {
|
|||
// const work1Contract = new web3.eth.Contract(work1Artifact.abi, contracts[chainId].Work1);
|
||||
|
||||
// const fetchPrice = async () => {
|
||||
// const priceWei = await work1Contract.methods.price().call();
|
||||
// setWork1Price(web3.utils.fromWei(priceWei, 'ether'));
|
||||
// };
|
||||
|
||||
const fetchReputation = async () => {
|
||||
setReputation(await DAOContract.methods.balanceOf(account).call());
|
||||
reputation.current = Number(await DAOContract.methods.balanceOf(account).call());
|
||||
setTotalReputation(await DAOContract.methods.totalSupply().call());
|
||||
};
|
||||
|
||||
|
@ -70,13 +78,60 @@ function App() {
|
|||
setPosts(fetchedPosts);
|
||||
};
|
||||
|
||||
const stake = async (poolIndex, amount, inFavor) => {
|
||||
console.log(`Attempting to stake ${amount} ${inFavor ? 'for' : 'against'} pool ${poolIndex}`);
|
||||
await DAOContract.methods.stake(poolIndex, amount, inFavor).send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
});
|
||||
|
||||
// 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.
|
||||
reputation.current = Number(reputation.current) - Number(stake);
|
||||
};
|
||||
|
||||
const stakeAllInFavor = (poolIndex) => stake(poolIndex, reputation.current, true);
|
||||
|
||||
const fetchValidationPools = async () => {
|
||||
// TODO: Pagination
|
||||
// TODO: Memoization
|
||||
// TODO: Caching
|
||||
const count = await DAOContract.methods.validationPoolCount().call();
|
||||
const promises = [];
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(DAOContract.methods.validationPools(i).call());
|
||||
}
|
||||
const pools = await Promise.all(promises);
|
||||
const pools = (await Promise.all(promises)).map((p) => {
|
||||
const pool = p;
|
||||
pool.status = getStatus(pool);
|
||||
const timeRemaining = new Date(Number(pool.endTime) * 1000) - new Date();
|
||||
if (timeRemaining > 0 && !pool.resolved && reputation.current
|
||||
&& !stakedPools.current.includes(pool.id)) {
|
||||
// Naievely stake all reputation that this validation pool is valid.
|
||||
// This is the greediest possible strategy.
|
||||
// Staking reputation transfers it, thus it's important we update our reputation
|
||||
// locally before hearing back from the server -- since blockchains are slow.
|
||||
// Note that this means refresing the page will re-send any pending staking operations.
|
||||
stakeAllInFavor(pool.id);
|
||||
stakedPools.current = stakedPools.current.concat(pool.id);
|
||||
}
|
||||
|
||||
// TODO: When remaing time expires, we want to update the status for this pool
|
||||
// if (timeRemaining > 0) {
|
||||
// setTimeout(() => {
|
||||
// pool.status = getStatus(pool);
|
||||
// setValidationPools((currentPools) => {
|
||||
// const newPools = currentPools;
|
||||
// newPools[pool.id] = pool;
|
||||
// return newPools;
|
||||
// });
|
||||
// console.log(`attepted to update pool status: ${pool.status}`);
|
||||
// }, timeRemaining);
|
||||
// }
|
||||
|
||||
return pool;
|
||||
});
|
||||
setValidationPools(pools);
|
||||
};
|
||||
|
||||
|
@ -102,7 +157,7 @@ function App() {
|
|||
fetchReputation();
|
||||
fetchValidationPools();
|
||||
});
|
||||
}, [provider, account, chainId]);
|
||||
}, [provider, account, chainId, reputation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!provider || balance === undefined) return;
|
||||
|
@ -134,7 +189,7 @@ function App() {
|
|||
};
|
||||
|
||||
const initiateValidationPool = async (postIndex) => {
|
||||
const poolDuration = 0;
|
||||
const poolDuration = 60;
|
||||
await DAO.methods.initiateValidationPool(postIndex, poolDuration).send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
|
@ -183,7 +238,7 @@ function App() {
|
|||
{`Balance: ${balanceEther} ETH`}
|
||||
</div>
|
||||
<div>
|
||||
{`Your REP: ${reputation}`}
|
||||
{`Your REP: ${reputation.current}`}
|
||||
</div>
|
||||
<div>
|
||||
{`Total REP: ${totalReputation}`}
|
||||
|
@ -232,6 +287,11 @@ function App() {
|
|||
<th>Fee</th>
|
||||
<th>Duration</th>
|
||||
<th>End Time</th>
|
||||
<th>
|
||||
Stake
|
||||
<br />
|
||||
Count
|
||||
</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
|
@ -244,11 +304,8 @@ function App() {
|
|||
<td>{pool.fee.toString()}</td>
|
||||
<td>{pool.duration.toString()}</td>
|
||||
<td>{new Date(Number(pool.endTime) * 1000).toLocaleString()}</td>
|
||||
<td>
|
||||
{pool.resolved && (pool.outcome ? 'Accepted' : 'Rejected')}
|
||||
{!pool.resolved && new Date() < new Date(Number(pool.endTime) * 1000) && 'In Progress'}
|
||||
{!pool.resolved && new Date() >= new Date(Number(pool.endTime) * 1000) && 'Ready to Evaluate'}
|
||||
</td>
|
||||
<td>{pool.stakeCount.toString()}</td>
|
||||
<td>{pool.status}</td>
|
||||
<td>
|
||||
{!pool.resolved && (
|
||||
<Button onClick={() => evaluateOutcome(pool.id)}>
|
||||
|
|
|
@ -57,6 +57,9 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
// TODO: Make parameters adjustable
|
||||
// TODO: Add forum parameters
|
||||
|
||||
uint public constant minDuration = 60; // 1 minute
|
||||
uint public constant maxDuration = 86400; // 1 day
|
||||
|
||||
event PostAdded(uint postIndex);
|
||||
event ValidationPoolInitiated(uint poolIndex);
|
||||
event ValidationPoolResolved(uint poolIndex, bool votePasses);
|
||||
|
@ -78,6 +81,8 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
uint duration
|
||||
) public payable returns (uint poolIndex) {
|
||||
require(msg.value > 0, "Fee is required to initiate validation pool");
|
||||
require(duration >= minDuration, "Duration is too short");
|
||||
require(duration <= maxDuration, "Duration is too long");
|
||||
Post storage post = posts[postIndex];
|
||||
require(post.author != address(0), "Target post not found");
|
||||
poolIndex = validationPoolCount++;
|
||||
|
|
|
@ -63,6 +63,18 @@ describe('DAO', () => {
|
|||
await expect(init()).to.be.revertedWith('Fee is required to initiate validation pool');
|
||||
});
|
||||
|
||||
it('should not be able to initiate a validation pool with duration below minimum', async () => {
|
||||
const setup = await loadFixture(deploy);
|
||||
const init = () => setup.dao.initiateValidationPool(0, 59, { value: POOL_FEE });
|
||||
await expect(init()).to.be.revertedWith('Duration is too short');
|
||||
});
|
||||
|
||||
it('should not be able to initiate a validation pool with duration above maximum', async () => {
|
||||
const setup = await loadFixture(deploy);
|
||||
const init = () => setup.dao.initiateValidationPool(0, 86401, { value: POOL_FEE });
|
||||
await expect(init()).to.be.revertedWith('Duration is too long');
|
||||
});
|
||||
|
||||
it('should be able to initiate a second validation pool', async () => {
|
||||
const init = () => dao.initiateValidationPool(0, POOL_DURATION, { value: POOL_FEE });
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
|
|
|
@ -18,7 +18,8 @@ describe('Work1', () => {
|
|||
const work1 = await Work1.deploy(dao.target, WORK1_PRICE);
|
||||
|
||||
await dao.addPost(account1);
|
||||
await dao.initiateValidationPool(0, 0, { value: 100 });
|
||||
await dao.initiateValidationPool(0, 60, { value: 100 });
|
||||
await time.increase(61);
|
||||
await dao.evaluateOutcome(0);
|
||||
|
||||
return {
|
||||
|
|
Loading…
Reference in New Issue