index posts on chain by content id (hash)
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 41s Details

This commit is contained in:
Ladd Hoffman 2024-04-18 18:35:31 -05:00
parent 5581b24c6e
commit f24b6e4106
17 changed files with 218 additions and 225 deletions

View File

@ -81,22 +81,30 @@ function App() {
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 });
const fetchPost = useCallback(async (postId) => {
const p = await DAORef.current.methods.posts(postId).call();
dispatchPost({ type: 'updateById', item: p });
return p;
}, [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();
const promises = [];
let promises = [];
dispatchPost({ type: 'refresh' });
for (let i = 0; i < count; i += 1) {
promises.push(fetchPost(i));
promises.push(fetchPostId(i));
}
await Promise.all(promises);
}, [DAORef, dispatchPost, fetchPost]);
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) => {
@ -193,7 +201,7 @@ function App() {
DAOContract.events.PostAdded({ fromBlock: 'latest' }).on('data', (event) => {
console.log('event: post added');
fetchPost(event.returnValues.postIndex);
fetchPost(event.returnValues.id);
});
DAOContract.events.ValidationPoolInitiated({ fromBlock: 'latest' }).on('data', (event) => {
@ -263,10 +271,10 @@ function App() {
});
}, [provider, chainId]);
const initiateValidationPool = useCallback(async (postIndex, poolDuration) => {
const initiateValidationPool = useCallback(async (postId, poolDuration) => {
const web3 = new Web3(provider);
await DAO.methods.initiateValidationPool(
postIndex,
postId,
poolDuration ?? 3600,
[1, 3],
[1, 2],
@ -307,8 +315,8 @@ function App() {
const handleShowAddPost = () => setShowAddPost(true);
const handleShowViewPost = useCallback(async ({ contentId }) => {
const post = await Post.read(contentId);
const handleShowViewPost = useCallback(async ({ id }) => {
const post = await Post.read(id);
setViewPost(post);
setShowViewPost(true);
}, [setViewPost, setShowViewPost]);
@ -476,7 +484,7 @@ function App() {
{validationPools.filter((x) => !!x).map((pool) => (
<tr key={pool.id}>
<td>{pool.id.toString()}</td>
<td>{pool.postIndex.toString()}</td>
<td>{pool.postId}</td>
<td>{getAddressName(chainId, pool.sender)}</td>
<td>{pool.fee.toString()}</td>
<td>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -28,7 +28,7 @@ const getProposalStatus = (proposal) => {
function Proposals() {
const {
provider, chainId, account, reputation, posts,
provider, chainId, account, reputation,
} = useContext(Web3Context);
const [proposals, dispatchProposal] = useList();
const proposalsContract = useRef();
@ -96,11 +96,11 @@ function Proposals() {
};
const handleShowViewProposal = useCallback(async (proposal) => {
const { postIndex } = proposal;
const post = await Post.read(posts[postIndex].contentId);
const { postId } = proposal;
const post = await Post.read(postId);
setViewPost(post);
setShowViewProposal(true);
}, [posts, setViewPost, setShowViewProposal]);
}, [setViewPost, setShowViewProposal]);
const onSubmitProposal = useCallback(async (post) => {
const web3 = new Web3(provider);

View File

@ -27,17 +27,13 @@ contract Onboarding is WorkContract, IOnValidate {
request.status = WorkStatus.ApprovalSubmitted;
request.approval = approval;
// Make work evidence post
uint postIndex = dao.addPost(
stake.worker,
request.evidenceContentId,
request.citations
);
dao.addPost(stake.worker, request.evidenceContentId, request.citations);
emit WorkApprovalSubmitted(requestIndex, approval);
// Initiate validation pool
uint poolIndex = dao.initiateValidationPool{
value: request.fee - request.fee / 10
}(
postIndex,
request.evidenceContentId,
POOL_DURATION,
[uint256(1), uint256(3)],
[uint256(1), uint256(2)],
@ -75,13 +71,9 @@ contract Onboarding is WorkContract, IOnValidate {
return 1;
}
Citation[] memory emptyCitations;
uint postIndex = dao.addPost(
request.customer,
request.requestContentId,
emptyCitations
);
dao.addPost(request.customer, request.requestContentId, emptyCitations);
dao.initiateValidationPool{value: request.fee / 10}(
postIndex,
request.requestContentId,
POOL_DURATION,
[uint256(1), uint256(3)],
[uint256(1), uint256(2)],

View File

@ -38,7 +38,7 @@ contract Proposals is DAOContract, IOnValidate {
address sender;
uint fee;
uint remainingFee;
uint postIndex;
string postId;
uint startTime;
Stage stage;
mapping(address => uint) attestations;
@ -73,11 +73,11 @@ contract Proposals is DAOContract, IOnValidate {
// or support post lookup by contentId
// TODO: Take citations as a parameter
Citation[] memory emptyCitations;
uint postIndex = dao.addPost(author, contentId, emptyCitations);
dao.addPost(author, contentId, emptyCitations);
proposalIndex = proposalCount++;
Proposal storage proposal = proposals[proposalIndex];
proposal.sender = msg.sender;
proposal.postIndex = postIndex;
proposal.postId = contentId;
proposal.startTime = block.timestamp;
proposal.referenda[0].duration = durations[0];
proposal.referenda[1].duration = durations[1];
@ -138,7 +138,7 @@ contract Proposals is DAOContract, IOnValidate {
Proposal storage proposal = proposals[proposalIndex];
proposal.remainingFee -= fee;
uint poolIndex = dao.initiateValidationPool{value: fee}(
proposal.postIndex,
proposal.postId,
proposal.referenda[referendumIndex].duration,
referendaQuora[referendumIndex],
referendaWinRatio[referendumIndex],

View File

@ -179,15 +179,11 @@ abstract contract WorkContract is
request.status = WorkStatus.ApprovalSubmitted;
request.approval = approval;
// Make work evidence post
uint postIndex = dao.addPost(
stake.worker,
request.evidenceContentId,
request.citations
);
dao.addPost(stake.worker, request.evidenceContentId, request.citations);
emit WorkApprovalSubmitted(requestIndex, approval);
// Initiate validation pool
uint poolIndex = dao.initiateValidationPool{value: request.fee}(
postIndex,
request.evidenceContentId,
POOL_DURATION,
[uint256(1), uint256(3)],
[uint256(1), uint256(2)],

View File

@ -8,7 +8,6 @@ import "./Forum.sol";
import "../interfaces/IAcceptAvailability.sol";
import "hardhat/console.sol";
// TODO: consider dynamically constructing contract instances rather than merging at build time
contract DAO is Reputation, Forum, ValidationPools {
/// Authorize a contract to transfer REP, and call that contract's acceptAvailability method
function stakeAvailability(

View File

@ -6,24 +6,25 @@ import "hardhat/console.sol";
struct Citation {
int weightPercent;
uint targetPostIndex;
string targetPostId;
}
struct Post {
uint id;
string id;
address sender;
address author;
string contentId;
Citation[] citations;
uint reputation;
// TODO: timestamp
}
contract Forum is Reputation {
mapping(uint => Post) public posts;
mapping(string => Post) public posts;
string[] public postIds;
uint public postCount;
mapping(uint => mapping(uint => int)) _edgeBalances;
mapping(string => mapping(string => int)) _edgeBalances;
event PostAdded(uint postIndex);
event PostAdded(string id);
// Forum parameters
// TODO: Make depth limit configurable; take as param in _onValidatePost callback
@ -33,13 +34,17 @@ contract Forum is Reputation {
address author,
string calldata contentId,
Citation[] calldata citations
) external returns (uint postIndex) {
postIndex = postCount++;
Post storage post = posts[postIndex];
) external {
postCount++;
postIds.push(contentId);
Post storage post = posts[contentId];
require(
post.author == address(0),
"A post with this contentId already exists"
);
post.author = author;
post.sender = msg.sender;
post.id = postIndex;
post.contentId = contentId;
post.id = contentId;
for (uint i = 0; i < citations.length; i++) {
post.citations.push(citations[i]);
}
@ -60,22 +65,22 @@ contract Forum is Reputation {
totalCitationWeightNeg >= -100,
"Sum of negative citations must be >= -100"
);
emit PostAdded(postIndex);
emit PostAdded(contentId);
}
function _onValidatePost(uint postIndex, uint amount) internal {
_propagateReputation(postIndex, int(amount), false, 0);
function _onValidatePost(string memory postId, uint amount) internal {
_propagateReputation(postId, int(amount), false, 0);
}
function _handleCitation(
uint postIndex,
string memory postId,
Citation memory citation,
int amount,
bool initialNegative,
uint depth
) internal returns (int outboundAmount) {
outboundAmount = (amount * citation.weightPercent) / 100;
if (citation.targetPostIndex == type(uint256).max) {
if (bytes(citation.targetPostId).length == 0) {
// Incineration
require(
outboundAmount >= 0,
@ -84,9 +89,7 @@ contract Forum is Reputation {
_burn(address(this), uint(outboundAmount));
return outboundAmount;
}
int balanceToOutbound = _edgeBalances[postIndex][
citation.targetPostIndex
];
int balanceToOutbound = _edgeBalances[postId][citation.targetPostId];
if (initialNegative) {
if (outboundAmount < 0) {
outboundAmount = outboundAmount > -balanceToOutbound
@ -99,17 +102,17 @@ contract Forum is Reputation {
}
}
int refund = _propagateReputation(
citation.targetPostIndex,
citation.targetPostId,
outboundAmount,
initialNegative || (depth == 0 && citation.weightPercent < 0),
depth + 1
);
outboundAmount -= refund;
_edgeBalances[postIndex][citation.targetPostIndex] += outboundAmount;
_edgeBalances[postId][citation.targetPostId] += outboundAmount;
}
function _propagateReputation(
uint postIndex,
string memory postId,
int amount,
bool initialNegative,
uint depth
@ -117,12 +120,12 @@ contract Forum is Reputation {
if (depth >= depthLimit) {
return amount;
}
Post storage post = posts[postIndex];
Post storage post = posts[postId];
// Propagate negative citations first
for (uint i = 0; i < post.citations.length; i++) {
if (post.citations[i].weightPercent < 0) {
amount -= _handleCitation(
postIndex,
postId,
post.citations[i],
amount,
initialNegative,
@ -134,7 +137,7 @@ contract Forum is Reputation {
for (uint i = 0; i < post.citations.length; i++) {
if (post.citations[i].weightPercent > 0) {
amount -= _handleCitation(
postIndex,
postId,
post.citations[i],
amount,
initialNegative,

View File

@ -23,7 +23,7 @@ struct ValidationPoolParams {
struct ValidationPool {
uint id;
uint postIndex;
string postId;
address sender;
uint minted;
mapping(uint => ValidationPoolStake) stakes;
@ -97,7 +97,7 @@ contract ValidationPools is Reputation, Forum {
/// Accept fee to initiate a validation pool
function initiateValidationPool(
uint postIndex,
string calldata postId,
uint duration,
uint[2] calldata quorum, // [Numerator, Denominator]
uint[2] calldata winRatio, // [Numerator, Denominator]
@ -116,12 +116,12 @@ contract ValidationPools is Reputation, Forum {
require(quorum[0] <= quorum[1], "Quorum is greater than one");
require(winRatio[0] <= winRatio[1], "Win ratio is greater than one");
require(bindingPercent <= 100, "Binding percent must be <= 100");
Post storage post = posts[postIndex];
Post storage post = posts[postId];
require(post.author != address(0), "Target post not found");
poolIndex = validationPoolCount++;
ValidationPool storage pool = validationPools[poolIndex];
pool.sender = msg.sender;
pool.postIndex = postIndex;
pool.postId = postId;
pool.fee = msg.value;
pool.params.quorum = quorum;
pool.params.winRatio = winRatio;
@ -143,7 +143,7 @@ contract ValidationPools is Reputation, Forum {
/// 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];
Post storage post = posts[pool.postId];
require(pool.resolved == false, "Pool is already resolved");
uint stakedFor;
uint stakedAgainst;
@ -253,7 +253,7 @@ contract ValidationPools is Reputation, Forum {
}
// Transfer REP to the forum instead of to the author directly
_onValidatePost(pool.postIndex, pool.minted / 2 + remainder);
_onValidatePost(pool.postId, pool.minted / 2 + remainder);
} else {
// If vote does not pass, divide the losing stake among the winners
totalRewards += pool.minted;

View File

@ -23,12 +23,12 @@ describe('Forum', () => {
const emptyCallbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
const initiateValidationPool = ({
postIndex, duration,
postId, duration,
quorum, winRatio, bindingPercent,
redistributeLosingStakes, callbackOnValidate,
callbackData, fee,
} = {}) => dao.initiateValidationPool(
postIndex ?? 0,
postId,
duration ?? POOL_DURATION,
quorum ?? [1, 10],
winRatio ?? [1, 2],
@ -48,28 +48,28 @@ describe('Forum', () => {
it('should be able to add a post', async () => {
const contentId = 'some-id';
await expect(dao.addPost(account1, contentId, [])).to.emit(dao, 'PostAdded').withArgs(0);
const post = await dao.posts(0);
await expect(dao.addPost(account1, contentId, [])).to.emit(dao, 'PostAdded').withArgs('some-id');
const post = await dao.posts(contentId);
expect(post.author).to.equal(account1);
expect(post.sender).to.equal(account1);
expect(post.contentId).to.equal(contentId);
expect(post.id).to.equal(contentId);
});
it('should be able to add a post on behalf of another account', async () => {
const contentId = 'some-id';
await dao.addPost(account2, contentId, []);
const post = await dao.posts(0);
const post = await dao.posts(contentId);
expect(post.author).to.equal(account2);
expect(post.sender).to.equal(account1);
expect(post.contentId).to.equal(contentId);
expect(post.id).to.equal(contentId);
});
it('should be able to donate reputation via citations', async () => {
await dao.addPost(account1, 'content-id', []);
await dao.addPost(account2, 'second-content-id', [{ weightPercent: 50, targetPostIndex: 0 }]);
await initiateValidationPool({ postIndex: 1 });
await dao.addPost(account2, 'second-content-id', [{ weightPercent: 50, targetPostId: 'content-id' }]);
await initiateValidationPool({ postId: 'second-content-id' });
const pool = await dao.validationPools(0);
expect(pool.postIndex).to.equal(1);
expect(pool.postId).to.equal('second-content-id');
await dao.evaluateOutcome(0);
expect(await dao.balanceOf(account1)).to.equal(50);
expect(await dao.balanceOf(account2)).to.equal(50);
@ -77,41 +77,38 @@ describe('Forum', () => {
it('should be able to leach reputation via citations', async () => {
await dao.addPost(account1, 'content-id', []);
expect((await dao.posts(0)).reputation).to.equal(0);
await initiateValidationPool({ postIndex: 0 });
expect((await dao.posts('content-id')).reputation).to.equal(0);
await initiateValidationPool({ postId: 'content-id' });
await dao.evaluateOutcome(0);
expect(await dao.balanceOf(account1)).to.equal(100);
expect((await dao.posts(0)).reputation).to.equal(100);
await dao.addPost(account2, 'second-content-id', [{ weightPercent: -50, targetPostIndex: 0 }]);
expect((await dao.posts(1)).reputation).to.equal(0);
await initiateValidationPool({ postIndex: 1 });
expect((await dao.posts('content-id')).reputation).to.equal(100);
await dao.addPost(account2, 'second-content-id', [{ weightPercent: -50, targetPostId: 'content-id' }]);
expect((await dao.posts('second-content-id')).reputation).to.equal(0);
await initiateValidationPool({ postId: 'second-content-id' });
const pool = await dao.validationPools(1);
expect(pool.postIndex).to.equal(1);
expect(pool.postId).to.equal('second-content-id');
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(1);
expect(await dao.balanceOf(account1)).to.equal(50);
expect(await dao.balanceOf(account2)).to.equal(150);
expect((await dao.posts(0)).reputation).to.equal(50);
expect((await dao.posts(1)).reputation).to.equal(150);
expect((await dao.posts('content-id')).reputation).to.equal(50);
expect((await dao.posts('second-content-id')).reputation).to.equal(150);
});
it('should be able to redistribute power via citations', async () => {
console.log('First post');
await dao.addPost(account1, 'content-id', []);
await initiateValidationPool({ postIndex: 0 });
await initiateValidationPool({ postId: 'content-id' });
await dao.evaluateOutcome(0);
expect(await dao.balanceOf(account1)).to.equal(100);
console.log('Second post');
await dao.addPost(account2, 'second-content-id', []);
expect(await dao.balanceOf(account2)).to.equal(0);
console.log('Third post');
await dao.addPost(account3, 'third-content-id', [
{ weightPercent: 100, targetPostIndex: 1 },
{ weightPercent: -100, targetPostIndex: 0 },
{ weightPercent: -100, targetPostId: 'content-id' },
{ weightPercent: 100, targetPostId: 'second-content-id' },
]);
await initiateValidationPool({ postIndex: 2 });
await initiateValidationPool({ postId: 'third-content-id' });
const pool = await dao.validationPools(1);
expect(pool.postIndex).to.equal(2);
expect(pool.postId).to.equal('third-content-id');
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(1);
expect(await dao.balanceOf(account1)).to.equal(0);
@ -121,17 +118,17 @@ describe('Forum', () => {
it('should be able to reverse a negative citation with a negative citation', async () => {
await dao.addPost(account1, 'content-id', []);
await initiateValidationPool({ postIndex: 0 });
await initiateValidationPool({ postId: 'content-id' });
await dao.evaluateOutcome(0);
expect(await dao.balanceOf(account1)).to.equal(100);
await dao.addPost(account2, 'second-content-id', [{ weightPercent: -100, targetPostIndex: 0 }]);
await initiateValidationPool({ postIndex: 1 });
await dao.addPost(account2, 'second-content-id', [{ weightPercent: -100, targetPostId: 'content-id' }]);
await initiateValidationPool({ postId: 'second-content-id' });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(1);
expect(await dao.balanceOf(account1)).to.equal(0);
expect(await dao.balanceOf(account2)).to.equal(200);
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostIndex: 1 }]);
await initiateValidationPool({ postIndex: 2 });
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostId: 'second-content-id' }]);
await initiateValidationPool({ postId: 'third-content-id' });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(2);
expect(await dao.balanceOf(account1)).to.equal(100);
@ -141,17 +138,17 @@ describe('Forum', () => {
it('forum reputation rewards are shared with validation pool policing rewards', async () => {
await dao.addPost(account1, 'content-id', []);
await initiateValidationPool({ postIndex: 0 });
await initiateValidationPool({ postId: 'content-id' });
await dao.evaluateOutcome(0);
expect(await dao.balanceOf(account1)).to.equal(100);
await dao.addPost(account2, 'second-content-id', []);
await initiateValidationPool({ postIndex: 1 });
await initiateValidationPool({ postId: 'second-content-id' });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(1);
expect(await dao.balanceOf(account1)).to.equal(100);
expect(await dao.balanceOf(account2)).to.equal(100);
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostIndex: 1 }]);
await initiateValidationPool({ postIndex: 2 });
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostId: 'second-content-id' }]);
await initiateValidationPool({ postId: 'third-content-id' });
await dao.stakeOnValidationPool(2, 100, true);
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(2);
@ -163,17 +160,17 @@ describe('Forum', () => {
it('should limit effects of negative references on prior positive references', async () => {
console.log('First post');
await dao.addPost(account1, 'content-id', []);
await initiateValidationPool({ postIndex: 0 });
await initiateValidationPool({ postId: 'content-id' });
await dao.evaluateOutcome(0);
expect(await dao.balanceOf(account1)).to.equal(100);
await dao.addPost(account2, 'second-content-id', [{ weightPercent: 50, targetPostIndex: 0 }]);
await initiateValidationPool({ postIndex: 1 });
await dao.addPost(account2, 'second-content-id', [{ weightPercent: 50, targetPostId: 'content-id' }]);
await initiateValidationPool({ postId: 'second-content-id' });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(1);
expect(await dao.balanceOf(account1)).to.equal(150);
expect(await dao.balanceOf(account2)).to.equal(50);
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostIndex: 1 }]);
await initiateValidationPool({ postIndex: 2, fee: 200 });
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostId: 'second-content-id' }]);
await initiateValidationPool({ postId: 'third-content-id', fee: 200 });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(2);
expect(await dao.balanceOf(account1)).to.equal(100);
@ -183,17 +180,17 @@ describe('Forum', () => {
it('should limit effects of negative references on prior negative references', async () => {
await dao.addPost(account1, 'content-id', []);
await initiateValidationPool({ postIndex: 0 });
await initiateValidationPool({ postId: 'content-id' });
await dao.evaluateOutcome(0);
expect(await dao.balanceOf(account1)).to.equal(100);
await dao.addPost(account2, 'second-content-id', [{ weightPercent: -100, targetPostIndex: 0 }]);
await initiateValidationPool({ postIndex: 1 });
await dao.addPost(account2, 'second-content-id', [{ weightPercent: -100, targetPostId: 'content-id' }]);
await initiateValidationPool({ postId: 'second-content-id' });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(1);
expect(await dao.balanceOf(account1)).to.equal(0);
expect(await dao.balanceOf(account2)).to.equal(200);
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostIndex: 1 }]);
await initiateValidationPool({ postIndex: 2, fee: 200 });
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostId: 'second-content-id' }]);
await initiateValidationPool({ postId: 'third-content-id', fee: 200 });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(2);
expect(await dao.balanceOf(account1)).to.equal(100);
@ -203,16 +200,16 @@ describe('Forum', () => {
it('should enforce depth limit', async () => {
await dao.addPost(account1, 'content-id-1', []);
await dao.addPost(account1, 'content-id-2', [{ weightPercent: 100, targetPostIndex: 0 }]);
await dao.addPost(account1, 'content-id-3', [{ weightPercent: 100, targetPostIndex: 1 }]);
await dao.addPost(account1, 'content-id-4', [{ weightPercent: 100, targetPostIndex: 2 }]);
await initiateValidationPool({ postIndex: 3 });
await dao.addPost(account1, 'content-id-2', [{ weightPercent: 100, targetPostId: 'content-id-1' }]);
await dao.addPost(account1, 'content-id-3', [{ weightPercent: 100, targetPostId: 'content-id-2' }]);
await dao.addPost(account1, 'content-id-4', [{ weightPercent: 100, targetPostId: 'content-id-3' }]);
await initiateValidationPool({ postId: 'content-id-4' });
await dao.evaluateOutcome(0);
const posts = await Promise.all([
await dao.posts(0),
await dao.posts(1),
await dao.posts(2),
await dao.posts(3),
await dao.posts('content-id-1'),
await dao.posts('content-id-2'),
await dao.posts('content-id-3'),
await dao.posts('content-id-4'),
]);
expect(posts[0].reputation).to.equal(0);
expect(posts[1].reputation).to.equal(100);
@ -224,37 +221,37 @@ describe('Forum', () => {
await dao.addPost(account1, 'content-id-1', [
{
weightPercent: 50,
targetPostIndex: ethers.MaxUint256,
targetPostId: '',
},
]);
await initiateValidationPool({ postIndex: 0 });
await initiateValidationPool({ postId: 'content-id-1' });
expect(await dao.totalSupply()).to.equal(100);
await dao.evaluateOutcome(0);
expect((await dao.posts(0)).reputation).to.equal(50);
expect((await dao.posts('content-id-1')).reputation).to.equal(50);
expect(await dao.totalSupply()).to.equal(50);
});
describe('negative citation of a post, the author having already staked and lost reputation', async () => {
beforeEach(async () => {
await dao.addPost(account1, 'content-id', []);
await initiateValidationPool({ postIndex: 0 });
await initiateValidationPool({ postId: 'content-id' });
await dao.evaluateOutcome(0);
expect(await dao.balanceOf(account1)).to.equal(100);
expect(await dao.totalSupply()).to.equal(100);
expect((await dao.posts(0)).reputation).to.equal(100);
expect((await dao.posts('content-id')).reputation).to.equal(100);
await dao.addPost(account2, 'second-content-id', []);
await initiateValidationPool({ postIndex: 1 });
await initiateValidationPool({ postId: 'second-content-id' });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(1);
expect(await dao.balanceOf(account1)).to.equal(100);
expect(await dao.balanceOf(account2)).to.equal(100);
expect(await dao.totalSupply()).to.equal(200);
expect((await dao.posts(0)).reputation).to.equal(100);
expect((await dao.posts(1)).reputation).to.equal(100);
expect((await dao.posts('content-id')).reputation).to.equal(100);
expect((await dao.posts('second-content-id')).reputation).to.equal(100);
// account1 stakes and loses
await initiateValidationPool({ postIndex: 1 });
await initiateValidationPool({ postId: 'second-content-id' });
await dao.stakeOnValidationPool(2, 50, true);
await dao.connect(account2).stakeOnValidationPool(2, 60, false);
await time.increase(POOL_DURATION + 1);
@ -262,53 +259,53 @@ describe('Forum', () => {
expect(await dao.balanceOf(account1)).to.equal(50);
expect(await dao.balanceOf(account2)).to.equal(250);
expect(await dao.totalSupply()).to.equal(300);
expect((await dao.posts(0)).reputation).to.equal(100);
expect((await dao.posts(1)).reputation).to.equal(100);
expect((await dao.posts('content-id')).reputation).to.equal(100);
expect((await dao.posts('second-content-id')).reputation).to.equal(100);
});
it('author and post rep can be completely destroyed', async () => {
// account1's post is later strongly negatively referenced
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostIndex: 0 }]);
await initiateValidationPool({ postIndex: 2, fee: 200 });
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostId: 'content-id' }]);
await initiateValidationPool({ postId: 'third-content-id', fee: 200 });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(3);
expect(await dao.balanceOf(account1)).to.equal(0);
expect(await dao.balanceOf(account2)).to.equal(250);
expect(await dao.balanceOf(account3)).to.equal(250);
expect(await dao.totalSupply()).to.equal(500);
expect((await dao.posts(0)).reputation).to.equal(0);
expect((await dao.posts(1)).reputation).to.equal(100);
expect((await dao.posts(2)).reputation).to.equal(250);
expect((await dao.posts('content-id')).reputation).to.equal(0);
expect((await dao.posts('second-content-id')).reputation).to.equal(100);
expect((await dao.posts('third-content-id')).reputation).to.equal(250);
});
it('author rep can be destroyed while some post rep remains', async () => {
// account1's post is later strongly negatively referenced
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostIndex: 0 }]);
await initiateValidationPool({ postIndex: 2, fee: 70 });
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostId: 'content-id' }]);
await initiateValidationPool({ postId: 'third-content-id', fee: 70 });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(3);
expect(await dao.totalSupply()).to.equal(370);
expect(await dao.balanceOf(account1)).to.equal(0);
expect(await dao.balanceOf(account2)).to.equal(250);
expect(await dao.balanceOf(account3)).to.equal(120);
expect((await dao.posts(0)).reputation).to.equal(30);
expect((await dao.posts(1)).reputation).to.equal(100);
expect((await dao.posts(2)).reputation).to.equal(120);
expect((await dao.posts('content-id')).reputation).to.equal(30);
expect((await dao.posts('second-content-id')).reputation).to.equal(100);
expect((await dao.posts('third-content-id')).reputation).to.equal(120);
});
it('author rep can be destroyed while some post rep remains (odd amount)', async () => {
// account1's post is later strongly negatively referenced
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostIndex: 0 }]);
await initiateValidationPool({ postIndex: 2, fee: 75 });
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostId: 'content-id' }]);
await initiateValidationPool({ postId: 'third-content-id', fee: 75 });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(3);
expect(await dao.totalSupply()).to.equal(375);
expect(await dao.balanceOf(account1)).to.equal(0);
expect(await dao.balanceOf(account2)).to.equal(250);
expect(await dao.balanceOf(account3)).to.equal(125);
expect((await dao.posts(0)).reputation).to.equal(25);
expect((await dao.posts(1)).reputation).to.equal(100);
expect((await dao.posts(2)).reputation).to.equal(125);
expect((await dao.posts('content-id')).reputation).to.equal(25);
expect((await dao.posts('second-content-id')).reputation).to.equal(100);
expect((await dao.posts('third-content-id')).reputation).to.equal(125);
});
});
});

View File

@ -22,7 +22,7 @@ describe('Onboarding', () => {
await dao.addPost(account1, 'content-id', []);
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
await dao.initiateValidationPool(
0,
'content-id',
60,
[1, 3],
[1, 2],
@ -70,12 +70,12 @@ describe('Onboarding', () => {
await expect(onboarding.submitWorkApproval(0, true))
.to.emit(dao, 'ValidationPoolInitiated').withArgs(1)
.to.emit(onboarding, 'WorkApprovalSubmitted').withArgs(0, true);
const post = await dao.posts(1);
const post = await dao.posts('evidence-content-id');
expect(post.author).to.equal(account1);
expect(post.sender).to.equal(onboarding.target);
expect(post.contentId).to.equal('evidence-content-id');
expect(post.id).to.equal('evidence-content-id');
const pool = await dao.validationPools(1);
expect(pool.postIndex).to.equal(1);
expect(pool.postId).to.equal('evidence-content-id');
expect(pool.fee).to.equal(PRICE * 0.9);
expect(pool.sender).to.equal(onboarding.target);
});
@ -120,12 +120,12 @@ describe('Onboarding', () => {
await time.increase(86401);
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
expect(await dao.postCount()).to.equal(3);
const post = await dao.posts(2);
const post = await dao.posts('req-content-id');
expect(post.author).to.equal(account2);
expect(post.sender).to.equal(onboarding.target);
expect(post.contentId).to.equal('req-content-id');
expect(post.id).to.equal('req-content-id');
const pool = await dao.validationPools(2);
expect(pool.postIndex).to.equal(2);
expect(pool.postId).to.equal('req-content-id');
expect(pool.fee).to.equal(PRICE * 0.1);
expect(pool.sender).to.equal(onboarding.target);
expect(pool.fee);

View File

@ -20,7 +20,7 @@ describe('Proposal', () => {
await dao.addPost(account2, 'some-other-content-id', []);
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
await dao.initiateValidationPool(
0,
'some-content-id',
60,
[1, 3],
[1, 2],
@ -31,7 +31,7 @@ describe('Proposal', () => {
{ value: 1000 },
);
await dao.initiateValidationPool(
1,
'some-other-content-id',
60,
[1, 3],
[1, 2],
@ -82,7 +82,7 @@ describe('Proposal', () => {
await proposals.propose('proposal-content-id', account1, [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);
expect(proposal.postId).to.equal('proposal-content-id');
expect(proposal.stage).to.equal(0);
});

View File

@ -20,12 +20,12 @@ describe('Validation Pools', () => {
const emptyCallbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
const initiateValidationPool = ({
postIndex, duration,
postId, duration,
quorum, winRatio, bindingPercent,
redistributeLosingStakes, callbackOnValidate,
callbackData, fee,
} = {}) => dao.initiateValidationPool(
postIndex ?? 0,
postId ?? 'content-id',
duration ?? POOL_DURATION,
quorum ?? [1, 3],
winRatio ?? [1, 2],
@ -88,7 +88,7 @@ describe('Validation Pools', () => {
const pool = await dao.validationPools(0);
expect(pool).to.exist;
expect(pool.params.duration).to.equal(POOL_DURATION);
expect(pool.postIndex).to.equal(0);
expect(pool.postId).to.equal('content-id');
expect(pool.resolved).to.be.false;
expect(pool.sender).to.equal(account1);
});
@ -167,17 +167,7 @@ describe('Validation Pools', () => {
});
it('should be able to evaluate outcome of second validation pool', async () => {
const init = () => dao.initiateValidationPool(
0,
POOL_DURATION,
[1, 3],
[1, 2],
100,
true,
false,
emptyCallbackData,
{ value: POOL_FEE },
);
const init = () => initiateValidationPool();
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
expect(await dao.validationPoolCount()).to.equal(2);
time.increase(POOL_DURATION + 1);
@ -202,8 +192,8 @@ describe('Validation Pools', () => {
beforeEach(async () => {
time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(0);
await dao.addPost(account2, 'content-id', []);
const init = () => initiateValidationPool({ postIndex: 1 });
await dao.addPost(account2, 'content-id-2', []);
const init = () => initiateValidationPool({ postId: 'content-id-2' });
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(1);

View File

@ -22,7 +22,7 @@ describe('Work1', () => {
await dao.addPost(account1, 'some-content-id', []);
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
await dao.initiateValidationPool(
0,
'some-content-id',
60,
[1, 3],
[1, 2],
@ -211,14 +211,14 @@ describe('Work1', () => {
.to.emit(work1, 'WorkApprovalSubmitted').withArgs(0, true);
expect(await dao.balanceOf(work1.target)).to.equal(0);
expect(await dao.balanceOf(account1)).to.equal(100);
const post = await dao.posts(1);
const post = await dao.posts('evidence-content-id');
expect(post.author).to.equal(account1);
expect(post.sender).to.equal(work1.target);
expect(post.contentId).to.equal('evidence-content-id');
expect(post.id).to.equal('evidence-content-id');
const pool = await dao.validationPools(1);
expect(pool.fee).to.equal(WORK1_PRICE);
expect(pool.sender).to.equal(work1.target);
expect(pool.postIndex).to.equal(1);
expect(pool.postId).to.equal('evidence-content-id');
expect(pool.stakeCount).to.equal(1);
await time.increase(86401);
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true, true);