UI: list posts and pools
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 38s Details

This commit is contained in:
Ladd Hoffman 2024-03-12 17:53:04 -05:00
parent cb8d430d71
commit 123138935d
5 changed files with 341 additions and 56 deletions

View File

@ -8,8 +8,8 @@ import DAOArtifact from './assets/DAO.json';
const contracts = {
'0x539': { // Hardhat
DAO: '0x7209aa5b0B91700615bA982e4E4F1Eb967E31cf1',
Work1: '0x4a3f03f50b542BD27DbcaDAF9A3d608AE4f5AACc',
DAO: '0x8d914D38dD301FC4606f5aa9fEcF8A76389020d3',
Work1: '0x050C420Cc4995B41217Eba1B54B82Fd5687e9139',
},
'0xaa36a7': { // Sepolia
DAO: '0x8d914D38dD301FC4606f5aa9fEcF8A76389020d3',
@ -28,9 +28,8 @@ function App() {
const [balanceEther, setBalanceEther] = useState();
const [reputation, setReputation] = useState();
const [totalReputation, setTotalReputation] = useState();
const [validationPoolCount, setValidationPoolCount] = useState();
const [latestPoolIndex, setLatestPoolIndex] = useState();
const [votePasses, setVotePasses] = useState();
const [posts, setPosts] = useState([]);
const [validationPools, setValidationPools] = useState([]);
// const watchReputationToken = useCallback(async () => {
// await provider.request({
@ -57,31 +56,47 @@ function App() {
// };
const fetchReputation = async () => {
console.log(`fetchReputation, account: ${account}`);
setReputation(await DAOContract.methods.balanceOf(account).call());
setTotalReputation(await DAOContract.methods.totalSupply().call());
};
const fetchValidationPoolCount = async () => {
setValidationPoolCount(await DAOContract.methods.validationPoolCount().call());
const fetchPosts = async () => {
const count = await DAOContract.methods.postCount().call();
const promises = [];
for (let i = 0; i < count; i += 1) {
promises.push(DAOContract.methods.posts(i).call());
}
const fetchedPosts = await Promise.all(promises);
setPosts(fetchedPosts);
};
const fetchValidationPools = async () => {
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);
setValidationPools(pools);
};
// fetchPrice();
fetchReputation();
fetchValidationPoolCount();
fetchPosts();
fetchValidationPools();
// setWork1(work1Contract);
setDAO(DAOContract);
DAOContract.events.ValidationPoolInitiated({ fromBlock: 'latest' }).on('data', (event) => {
console.log('event: validation pool initiated', event);
setLatestPoolIndex(event.returnValues.poolIndex);
fetchValidationPoolCount();
fetchPosts();
fetchValidationPools();
});
DAOContract.events.ValidationPoolResolved({ fromBlock: 'latest' }).on('data', (event) => {
console.log('event: validation pool resolved', event);
setVotePasses(event.returnValues.votePasses);
fetchReputation();
fetchValidationPools();
});
}, [provider, account, chainId]);
@ -107,17 +122,24 @@ function App() {
}
};
const initiateValidationPool = async () => {
const addPost = async () => {
await DAO.methods.addPost(account).send({
from: account,
gas: 1000000,
});
};
const initiateValidationPool = async (postIndex) => {
const poolDuration = 0;
await DAO.methods.initiateValidationPool(account, poolDuration).send({
await DAO.methods.initiateValidationPool(postIndex, poolDuration).send({
from: account,
gas: 1000000,
value: 100,
});
};
const evaluateOutcome = async () => {
await DAO.methods.evaluateOutcome(latestPoolIndex).send({
const evaluateOutcome = async (poolIndex) => {
await DAO.methods.evaluateOutcome(poolIndex).send({
from: account,
gas: 1000000,
});
@ -165,16 +187,75 @@ function App() {
<Button onClick={() => disconnect()}>Disconnect</Button>
</div>
<div>
{`Validation Pool Count: ${validationPoolCount}`}
{`Posts count: ${posts.length}`}
</div>
<div>
<Button onClick={() => initiateValidationPool()}>Initiate Validation Pool</Button>
<table className="table">
<thead>
<tr>
<th>ID</th>
<th>Author</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{posts.map((post) => (
<tr key={post.id}>
<td>{post.id.toString()}</td>
<td>{post.author}</td>
<td>
<Button onClick={() => initiateValidationPool(post.id)}>
Initiate Validation Pool
</Button>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div>
<Button onClick={() => evaluateOutcome()}>Evaluate Outcome</Button>
<Button onClick={() => addPost()}>Add Post</Button>
</div>
<div>
{`Outcome: ${votePasses}`}
{`Validation Pool Count: ${validationPools.length}`}
</div>
<div>
<table className="table">
<thead>
<tr>
<th>ID</th>
<th>Post ID</th>
<th>Fee</th>
<th>Duration</th>
<th>End Time</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{validationPools.map((pool) => (
<tr key={pool.id}>
<td>{pool.id.toString()}</td>
<td>{pool.postIndex.toString()}</td>
<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.resolved && (
<Button onClick={() => evaluateOutcome(pool.id)}>
Evaluate Outcome
</Button>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
<div>
{ /* `Work1 Price: ${work1Price} ETH` */ }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -7,18 +7,21 @@ import "./IAcceptAvailability.sol";
import "hardhat/console.sol";
struct Post {
uint id;
address sender;
address author;
}
struct Stake {
uint id;
bool inFavor;
uint256 amount;
address sender;
}
struct ValidationPool {
Post post;
uint id;
uint postIndex;
mapping(uint => Stake) stakes;
uint stakeCount;
uint256 fee;
@ -62,6 +65,7 @@ contract DAO is ERC20("Reputation", "REP") {
Post storage post = posts[postIndex];
post.author = author;
post.sender = msg.sender;
post.id = postIndex;
}
/// Accept fee to initiate a validation pool
@ -72,22 +76,25 @@ contract DAO is ERC20("Reputation", "REP") {
uint duration
) public payable returns (uint poolIndex) {
require(msg.value > 0, "Fee is required to initiate validation pool");
Post storage post = posts[postIndex];
require(post.author != address(0), "Target post not found");
poolIndex = validationPoolCount++;
ValidationPool storage pool = validationPools[poolIndex];
pool.post = posts[postIndex];
pool.postIndex = postIndex;
pool.fee = msg.value;
pool.duration = duration;
pool.endTime = block.timestamp + duration;
pool.id = poolIndex;
// Because we need to stake part of the mited value for the pool an part against,
// we mint two new tokens.
// Here we assume a minting ratio of 1, and a stakeForAuthor ratio of 0.5
// Implementing this with adjustable parameters will require more advanced fixed point math.
// TODO: Make minting ratio an adjustable parameter
// TODO: Make stakeForAuthor an adjustable parameter
_mint(pool.post.author, msg.value);
_mint(post.author, msg.value);
// TODO: We need a way to exclude this pending reputation from the total supply when computing fee distribution
_stake(pool, pool.post.author, msg.value / 2, true);
_stake(pool, pool.post.author, msg.value / 2, false);
_stake(pool, post.author, msg.value / 2, true);
_stake(pool, post.author, msg.value / 2, false);
emit ValidationPoolInitiated(poolIndex);
}
@ -100,10 +107,12 @@ contract DAO is ERC20("Reputation", "REP") {
) internal {
require(block.timestamp <= pool.endTime, "Pool end time has passed");
_transfer(sender, address(this), amount);
Stake storage s = pool.stakes[pool.stakeCount++];
uint stakeIndex = pool.stakeCount++;
Stake storage s = pool.stakes[stakeIndex];
s.sender = sender;
s.inFavor = inFavor;
s.amount = amount;
s.id = stakeIndex;
}
/// Accept reputation stakes toward a validation pool
@ -115,6 +124,7 @@ contract DAO is ERC20("Reputation", "REP") {
/// Evaluate outcome of a validation pool
function evaluateOutcome(uint poolIndex) public returns (bool votePasses) {
ValidationPool storage pool = validationPools[poolIndex];
Post storage post = posts[pool.postIndex];
require(
block.timestamp > pool.endTime,
"Pool end time has not yet arrived"
@ -137,11 +147,12 @@ contract DAO is ERC20("Reputation", "REP") {
// This is especially important so that the DAO's first pool can pass,
// when no reputation has yet been minted.
votePasses = stakedFor >= stakedAgainst;
if (votePasses && !isMember[pool.post.author]) {
members[memberCount++] = pool.post.author;
isMember[pool.post.author] = true;
if (votePasses && !isMember[post.author]) {
members[memberCount++] = post.author;
isMember[post.author] = true;
}
pool.resolved = true;
pool.outcome = votePasses;
emit ValidationPoolResolved(poolIndex, votePasses);
// Value of losing stakes should be di stributed among winners, in proportion to their stakes
uint256 amountFromWinners = votePasses ? stakedFor : stakedAgainst;
@ -159,7 +170,7 @@ contract DAO is ERC20("Reputation", "REP") {
// Due to rounding, there may be some reward left over. Include this as a reward to the author.
uint256 remainder = amountFromLosers - totalRewards;
if (remainder > 0) {
_transfer(address(this), pool.post.author, remainder);
_transfer(address(this), post.author, remainder);
}
// Distribute fee proportionatly among all reputation holders
for (uint i = 0; i < memberCount; i++) {

View File

@ -73,6 +73,8 @@ describe('DAO', () => {
const pool = await dao.validationPools(0);
expect(pool).to.exist;
expect(pool.duration).to.equal(POOL_DURATION);
expect(pool.postIndex).to.equal(0);
expect(pool.resolved).to.be.false;
});
});
@ -86,6 +88,9 @@ describe('DAO', () => {
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true);
expect(await dao.memberCount()).to.equal(1);
expect(await dao.balanceOf(account1)).to.equal(100);
const pool = await dao.validationPools(0);
expect(pool.resolved).to.be.true;
expect(pool.outcome).to.be.true;
});
it('should not be able to evaluate outcome more than once', async () => {
@ -139,6 +144,8 @@ describe('DAO', () => {
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, false);
expect(await dao.balanceOf(dao.target)).to.equal(0);
expect(await dao.balanceOf(account1)).to.equal(200);
const pool = await dao.validationPools(1);
expect(pool.outcome).to.be.false;
});
});
});