From da20410f874c7907de252d14c342206941e3c33e Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Fri, 28 Jun 2024 13:44:18 -0500 Subject: [PATCH] update DAO to use global forum --- ethereum/contracts/GlobalForum.sol | 25 +- ethereum/contracts/Onboarding.sol | 14 +- ethereum/contracts/Proposals.sol | 2 +- ethereum/contracts/RollableWork.sol | 5 +- ethereum/contracts/Work.sol | 6 +- ethereum/contracts/Work1.sol | 9 +- ethereum/contracts/Work2.sol | 3 +- ethereum/contracts/core/Bench.sol | 141 +++++++++- ethereum/contracts/core/DAO.sol | 69 ++--- ethereum/contracts/core/Forum.sol | 251 ------------------ ethereum/scripts/deploy-dao.js | 4 +- ethereum/scripts/deploy.js | 6 +- .../scripts/util/deploy-core-contracts.js | 7 +- ethereum/scripts/util/deploy-work-contract.js | 1 + ethereum/test/Forum.js | 109 +++----- ethereum/test/Onboarding.js | 39 ++- ethereum/test/Proposals.js | 20 +- ethereum/test/ValidationPools.js | 11 +- ethereum/test/Work1.js | 21 +- ethereum/test/util/deploy-dao.js | 8 +- 20 files changed, 297 insertions(+), 454 deletions(-) delete mode 100644 ethereum/contracts/core/Forum.sol diff --git a/ethereum/contracts/GlobalForum.sol b/ethereum/contracts/GlobalForum.sol index dc063f5..d6213ff 100644 --- a/ethereum/contracts/GlobalForum.sol +++ b/ethereum/contracts/GlobalForum.sol @@ -20,17 +20,16 @@ struct Post { } contract GlobalForum { - mapping(string => Post) public posts; + mapping(string => Post) posts; string[] public postIds; uint public postCount; event PostAdded(string id); function addPost( - string calldata postId, Author[] calldata authors, - Reference[] calldata references, - string calldata content + string calldata postId, + Reference[] calldata references ) external { require(authors.length > 0, "Post must include at least one author"); postCount++; @@ -42,7 +41,6 @@ contract GlobalForum { ); post.sender = msg.sender; post.id = postId; - post.content = content; uint authorTotalWeightPPM; for (uint i = 0; i < authors.length; i++) { authorTotalWeightPPM += authors[i].weightPPM; @@ -87,4 +85,21 @@ contract GlobalForum { Post storage post = posts[postId]; return post.authors; } + + function getPost( + string calldata postId + ) + external + view + returns ( + Author[] memory authors, + Reference[] memory references, + address sender + ) + { + Post storage post = posts[postId]; + authors = post.authors; + references = post.references; + sender = post.sender; + } } diff --git a/ethereum/contracts/Onboarding.sol b/ethereum/contracts/Onboarding.sol index cfd765c..5045c95 100644 --- a/ethereum/contracts/Onboarding.sol +++ b/ethereum/contracts/Onboarding.sol @@ -2,16 +2,16 @@ pragma solidity ^0.8.24; import "./core/DAO.sol"; -import "./core/Forum.sol"; import "./Work.sol"; import "./interfaces/IOnValidate.sol"; contract Onboarding is Work, IOnValidate { constructor( - DAO dao_, - Proposals proposals_, - uint price_ - ) Work(dao_, proposals_, price_) {} + DAO dao, + GlobalForum forum, + Proposals proposals, + uint price + ) Work(dao, forum, proposals, price) {} /// Accept work approval/disapproval from customer function submitWorkApproval( @@ -29,7 +29,7 @@ contract Onboarding is Work, IOnValidate { // Make work evidence post Author[] memory authors = new Author[](1); authors[0] = Author(1000000, stake.worker); - dao.addPost(authors, request.evidencePostId, request.references); + forum.addPost(authors, request.evidencePostId, request.references); emit WorkApprovalSubmitted(requestIndex, approval); // Initiate validation pool uint poolIndex = dao.initiateValidationPool{ @@ -76,7 +76,7 @@ contract Onboarding is Work, IOnValidate { Reference[] memory emptyReferences; Author[] memory authors = new Author[](1); authors[0] = Author(1000000, request.customer); - dao.addPost(authors, request.requestPostId, emptyReferences); + forum.addPost(authors, request.requestPostId, emptyReferences); dao.initiateValidationPool{value: request.fee / 10}( request.requestPostId, POOL_DURATION, diff --git a/ethereum/contracts/Proposals.sol b/ethereum/contracts/Proposals.sol index 5ea7d7b..92ba2da 100644 --- a/ethereum/contracts/Proposals.sol +++ b/ethereum/contracts/Proposals.sol @@ -59,7 +59,7 @@ contract Proposals is DAOContract, IOnValidate { // TODO receive : we want to be able to accept refunds from validation pools - /// Submit a post as a proposal. DAO.addPost should be called before this. + /// Submit a post as a proposal. forum.addPost should be called before this. function propose( string calldata postId, uint[3] calldata durations, diff --git a/ethereum/contracts/RollableWork.sol b/ethereum/contracts/RollableWork.sol index 6740260..512bab4 100644 --- a/ethereum/contracts/RollableWork.sol +++ b/ethereum/contracts/RollableWork.sol @@ -9,10 +9,11 @@ abstract contract RollableWork is Work { constructor( DAO dao, + GlobalForum forum, Proposals proposalsContract, Rollup rollupContract_, uint price - ) Work(dao, proposalsContract, price) { + ) Work(dao, forum, proposalsContract, price) { rollupContract = rollupContract_; } @@ -34,7 +35,7 @@ abstract contract RollableWork is Work { // Make work evidence post Author[] memory authors = new Author[](1); authors[0] = Author(1000000, stake.worker); - dao.addPost(authors, request.evidencePostId, request.references); + forum.addPost(authors, request.evidencePostId, request.references); // send worker stakes and customer fee to rollup contract dao.forwardAllowance( diff --git a/ethereum/contracts/Work.sol b/ethereum/contracts/Work.sol index e967a4e..47c469c 100644 --- a/ethereum/contracts/Work.sol +++ b/ethereum/contracts/Work.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.24; import "./core/DAO.sol"; -import "./core/Forum.sol"; import "./Availability.sol"; import "./Proposals.sol"; import "./interfaces/IOnProposalAccepted.sol"; @@ -31,6 +30,7 @@ abstract contract Work is Availability, IOnProposalAccepted { uint proposalIndex; } + GlobalForum forum; Proposals proposalsContract; uint public price; mapping(uint => PriceProposal) public priceProposals; @@ -48,11 +48,13 @@ abstract contract Work is Availability, IOnProposalAccepted { constructor( DAO dao, + GlobalForum forum_, Proposals proposalsContract_, uint price_ ) Availability(dao) { price = price_; proposalsContract = proposalsContract_; + forum = forum_; } /// Accept work request with fee @@ -107,7 +109,7 @@ abstract contract Work is Availability, IOnProposalAccepted { // Make work evidence post Author[] memory authors = new Author[](1); authors[0] = Author(1000000, stake.worker); - dao.addPost(authors, request.evidencePostId, request.references); + forum.addPost(authors, request.evidencePostId, request.references); emit WorkApprovalSubmitted(requestIndex, approval); // Initiate validation pool uint poolIndex = dao.initiateValidationPool{value: request.fee}( diff --git a/ethereum/contracts/Work1.sol b/ethereum/contracts/Work1.sol index 1b9749e..6b191a8 100644 --- a/ethereum/contracts/Work1.sol +++ b/ethereum/contracts/Work1.sol @@ -7,8 +7,9 @@ import "./Proposals.sol"; contract Work1 is Work { constructor( - DAO dao_, - Proposals proposals_, - uint price_ - ) Work(dao_, proposals_, price_) {} + DAO dao, + GlobalForum forum, + Proposals proposals, + uint price + ) Work(dao, forum, proposals, price) {} } diff --git a/ethereum/contracts/Work2.sol b/ethereum/contracts/Work2.sol index ec75b06..2efd0b3 100644 --- a/ethereum/contracts/Work2.sol +++ b/ethereum/contracts/Work2.sol @@ -9,8 +9,9 @@ import "./Rollup.sol"; contract Work2 is RollableWork { constructor( DAO dao, + GlobalForum forum, Proposals proposals, Rollup rollup, uint price - ) RollableWork(dao, proposals, rollup, price) {} + ) RollableWork(dao, forum, proposals, rollup, price) {} } diff --git a/ethereum/contracts/core/Bench.sol b/ethereum/contracts/core/Bench.sol index 11e2d3f..2f753cd 100644 --- a/ethereum/contracts/core/Bench.sol +++ b/ethereum/contracts/core/Bench.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.24; import "./DAO.sol"; -import "./Forum.sol"; +import "../GlobalForum.sol"; struct ValidationPoolParams { uint duration; @@ -43,17 +43,26 @@ contract Bench { mapping(uint => Pool) public validationPools; uint public validationPoolCount; DAO dao; + GlobalForum forum; + // Validation Pool parameters uint constant minDuration = 1; // 1 second uint constant maxDuration = 365_000_000 days; // 1 million years uint[2] minQuorum = [1, 10]; - function registerDAO(DAO dao_) external { + // Forum parameters + // TODO: Make depth limit configurable; take as param + uint depthLimit = 3; + + mapping(string => mapping(string => int)) _edgeBalances; + + function registerDAO(DAO dao_, GlobalForum forum_) external { require( address(dao) == address(0), "A DAO has already been registered" ); dao = dao_; + forum = forum_; } /// Register a stake for/against a validation pool @@ -245,9 +254,11 @@ contract Bench { } // Transfer REP to the forum instead of to the author directly - dao.propagateReputation( + propagateReputation( pool.props.postId, - int(pool.props.minted / 2 + remainder) + int(pool.props.minted / 2 + remainder), + false, + 0 ); } else { // If vote does not pass, divide the losing stake among the winners @@ -283,4 +294,126 @@ contract Bench { ); } } + + function _handleReference( + string memory postId, + Reference memory ref, + int amount, + bool initialNegative, + uint depth + ) internal returns (int outboundAmount) { + outboundAmount = (amount * ref.weightPPM) / 1000000; + if (bytes(ref.targetPostId).length == 0) { + // Incineration + require( + outboundAmount >= 0, + "Leaching from incinerator is forbidden" + ); + dao.burn(address(dao), uint(outboundAmount)); + return outboundAmount; + } + int balanceToOutbound = _edgeBalances[postId][ref.targetPostId]; + if (initialNegative) { + if (outboundAmount < 0) { + outboundAmount = outboundAmount > -balanceToOutbound + ? outboundAmount + : -balanceToOutbound; + } else { + outboundAmount = outboundAmount < -balanceToOutbound + ? outboundAmount + : -balanceToOutbound; + } + } + int refund = propagateReputation( + ref.targetPostId, + outboundAmount, + initialNegative || (depth == 0 && ref.weightPPM < 0), + depth + 1 + ); + outboundAmount -= refund; + _edgeBalances[postId][ref.targetPostId] += outboundAmount; + } + + function _distributeAmongAuthors( + Author[] memory authors, + int amount + ) internal returns (int refund) { + int allocated; + + for (uint i = 0; i < authors.length; i++) { + dao.registerMember(authors[i].authorAddress); + } + for (uint i = 0; i < authors.length; i++) { + Author memory author = authors[i]; + int share; + if (i < authors.length - 1) { + share = (amount * int(author.weightPPM)) / 1000000; + allocated += share; + } else { + // For the last author, allocate the remainder. + share = amount - allocated; + } + if (share > 0) { + dao.update(address(dao), author.authorAddress, uint(share)); + } else if (dao.balanceOf(author.authorAddress) < uint(-share)) { + // Author has already lost some REP gained from this post. + // That means other DAO members have earned it for policing. + // We need to refund the difference here to ensure accurate bookkeeping + uint authorBalance = dao.balanceOf(author.authorAddress); + refund += share + int(authorBalance); + dao.update( + author.authorAddress, + address(dao), + dao.balanceOf(author.authorAddress) + ); + } else { + dao.update(author.authorAddress, address(dao), uint(-share)); + } + } + } + + function propagateReputation( + string memory postId, + int amount, + bool initialNegative, + uint depth + ) internal returns (int refundToInbound) { + if (depth >= depthLimit) { + return amount; + } + Reference[] memory references; + Author[] memory authors; + address sender; + (authors, references, sender) = forum.getPost(postId); + if (authors.length == 0) { + // We most likely got here via a reference to a post that hasn't been added yet. + // We support this scenario so that a reference graph can be imported one post at a time. + return amount; + } + // Propagate negative references first + for (uint i = 0; i < references.length; i++) { + if (references[i].weightPPM < 0) { + amount -= _handleReference( + postId, + references[i], + amount, + initialNegative, + depth + ); + } + } + // Now propagate positive references + for (uint i = 0; i < references.length; i++) { + if (references[i].weightPPM > 0) { + amount -= _handleReference( + postId, + references[i], + amount, + initialNegative, + depth + ); + } + } + refundToInbound = _distributeAmongAuthors(authors, amount); + } } diff --git a/ethereum/contracts/core/DAO.sol b/ethereum/contracts/core/DAO.sol index 88d4903..42d1dfd 100644 --- a/ethereum/contracts/core/DAO.sol +++ b/ethereum/contracts/core/DAO.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.24; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./Reputation.sol"; import "./Bench.sol"; -import "./Forum.sol"; +import "../GlobalForum.sol"; import "../interfaces/IAcceptAvailability.sol"; import "../interfaces/IOnValidate.sol"; @@ -12,27 +12,36 @@ import "hardhat/console.sol"; contract DAO { Reputation rep; - Forum forum; + GlobalForum forum; Bench bench; mapping(uint => address) public members; uint public memberCount; mapping(address => bool) public isMember; + event PostAdded(string id); event ValidationPoolInitiated(uint poolIndex); event ValidationPoolResolved( uint poolIndex, bool votePasses, bool quorumMet ); - event PostAdded(string id); + event LWValidationPoolInitiated(uint poolIndex); + event LWValidationPoolResolved( + uint poolIndex, + bool votePasses, + bool quorumMet + ); - constructor(Reputation reputation_, Forum forum_, Bench bench_) { + constructor(Reputation reputation_, Bench bench_, GlobalForum forum_) { rep = reputation_; - forum = forum_; bench = bench_; + forum = forum_; rep.registerDAO(this); - forum.registerDAO(this); - bench.registerDAO(this); + bench.registerDAO(this, forum); + } + + function emitPostAdded(string memory id) public { + emit PostAdded(id); } function emitValidationPoolInitiated(uint poolIndex) public { @@ -47,13 +56,13 @@ contract DAO { emit ValidationPoolResolved(poolIndex, votePasses, quorumMet); } - function emitPostAdded(string memory id) public { - emit PostAdded(id); + function emitLWValidationPoolInitiated(uint poolIndex) public { + emit LWValidationPoolInitiated(poolIndex); } function update(address from, address to, uint256 value) public { require( - msg.sender == address(forum) || msg.sender == address(bench), + msg.sender == address(bench), "Only DAO core contracts may call update" ); rep.update(from, to, value); @@ -61,7 +70,7 @@ contract DAO { function mint(address account, uint256 value) public { require( - msg.sender == address(forum) || msg.sender == address(bench), + msg.sender == address(bench), "Only DAO core contracts may call mint" ); rep.mint(account, value); @@ -69,7 +78,7 @@ contract DAO { function burn(address account, uint256 value) public { require( - msg.sender == address(forum) || msg.sender == address(bench), + msg.sender == address(bench), "Only DAO core contracts may call burn" ); rep.burn(account, value); @@ -77,7 +86,7 @@ contract DAO { function registerMember(address account) public { require( - msg.sender == address(forum) || msg.sender == address(bench), + msg.sender == address(bench), "Only DAO core contracts may call registerMember" ); if (!isMember[account]) { @@ -129,10 +138,6 @@ contract DAO { return true; } - function propagateReputation(string memory postId, int amount) public { - forum.propagateReputation(postId, amount, false, 0); - } - function distributeFeeAmongMembers() public payable { uint allocated; for (uint i = 0; i < memberCount; i++) { @@ -236,7 +241,7 @@ contract DAO { bytes calldata callbackData ) public { require( - msg.sender == address(forum) || msg.sender == address(bench), + msg.sender == address(bench), "Only DAO core contracts may call onValidate" ); IOnValidate(target).onValidate( @@ -247,34 +252,6 @@ contract DAO { callbackData ); } - - function addPost( - Author[] calldata authors, - string calldata postId, - Reference[] calldata references - ) public { - forum.addPost(msg.sender, authors, postId, references); - } - - function posts( - string calldata postId - ) public view returns (string memory id, address sender, uint reputation) { - return forum.posts(postId); - } - - function postCount() public view returns (uint) { - return forum.postCount(); - } - - function postIds(uint postIndex) public view returns (string memory) { - return forum.postIds(postIndex); - } - - function getPostAuthors( - string calldata postId - ) public view returns (Author[] memory) { - return forum.getPostAuthors(postId); - } } /// Convenience contract to extend for other contracts that will be initialized to diff --git a/ethereum/contracts/core/Forum.sol b/ethereum/contracts/core/Forum.sol deleted file mode 100644 index d3b3ada..0000000 --- a/ethereum/contracts/core/Forum.sol +++ /dev/null @@ -1,251 +0,0 @@ -// SPDX-License-Identifier: Unlicense -pragma solidity ^0.8.24; - -import "./DAO.sol"; - -struct Reference { - int weightPPM; - string targetPostId; -} - -struct Author { - uint weightPPM; - address authorAddress; -} - -struct Post { - string id; - address sender; - Author[] authors; - Reference[] references; - uint reputation; - // TODO: timestamp -} - -contract Forum { - mapping(string => Post) public posts; - string[] public postIds; - uint public postCount; - mapping(string => mapping(string => int)) _edgeBalances; - DAO dao; - - event PostAdded(string id); - - // Forum parameters - // TODO: Make depth limit configurable; take as param in _onValidatePost callback - uint depthLimit = 3; - - function registerDAO(DAO dao_) external { - require( - address(dao) == address(0), - "A DAO has already been registered" - ); - dao = dao_; - } - - function addPost( - address sender, - Author[] calldata authors, - string calldata postId, - Reference[] calldata references - ) external { - require( - msg.sender == address(dao), - "Only DAO contract may call addPost" - ); - require(authors.length > 0, "Post must include at least one author"); - postCount++; - postIds.push(postId); - Post storage post = posts[postId]; - require( - post.authors.length == 0, - "A post with this postId already exists" - ); - post.sender = sender; - post.id = postId; - uint authorTotalWeightPPM; - for (uint i = 0; i < authors.length; i++) { - authorTotalWeightPPM += authors[i].weightPPM; - post.authors.push(authors[i]); - } - require( - authorTotalWeightPPM == 1000000, - "Author weights must sum to 1000000" - ); - for (uint i = 0; i < references.length; i++) { - post.references.push(references[i]); - } - int totalReferenceWeightPos; - int totalReferenceWeightNeg; - for (uint i = 0; i < post.references.length; i++) { - int weight = post.references[i].weightPPM; - require( - weight >= -1000000, - "Each reference weight must be >= -1000000" - ); - require( - weight <= 1000000, - "Each reference weight must be <= 1000000" - ); - if (weight > 0) totalReferenceWeightPos += weight; - else totalReferenceWeightNeg += weight; - } - require( - totalReferenceWeightPos <= 1000000, - "Sum of positive references must be <= 1000000" - ); - require( - totalReferenceWeightNeg >= -1000000, - "Sum of negative references must be >= -1000000" - ); - dao.emitPostAdded(postId); - } - - function getPostAuthors( - string calldata postId - ) external view returns (Author[] memory) { - Post storage post = posts[postId]; - return post.authors; - } - - function _handleReference( - string memory postId, - Reference memory ref, - int amount, - bool initialNegative, - uint depth - ) internal returns (int outboundAmount) { - outboundAmount = (amount * ref.weightPPM) / 1000000; - if (bytes(ref.targetPostId).length == 0) { - // Incineration - require( - outboundAmount >= 0, - "Leaching from incinerator is forbidden" - ); - dao.burn(address(dao), uint(outboundAmount)); - return outboundAmount; - } - int balanceToOutbound = _edgeBalances[postId][ref.targetPostId]; - if (initialNegative) { - if (outboundAmount < 0) { - outboundAmount = outboundAmount > -balanceToOutbound - ? outboundAmount - : -balanceToOutbound; - } else { - outboundAmount = outboundAmount < -balanceToOutbound - ? outboundAmount - : -balanceToOutbound; - } - } - int refund = propagateReputation( - ref.targetPostId, - outboundAmount, - initialNegative || (depth == 0 && ref.weightPPM < 0), - depth + 1 - ); - outboundAmount -= refund; - _edgeBalances[postId][ref.targetPostId] += outboundAmount; - } - - function _distributeAmongAuthors( - Post memory post, - int amount - ) internal returns (int refund) { - int allocated; - - for (uint i = 0; i < post.authors.length; i++) { - dao.registerMember(post.authors[i].authorAddress); - } - for (uint i = 0; i < post.authors.length; i++) { - Author memory author = post.authors[i]; - int share; - if (i < post.authors.length - 1) { - share = (amount * int(author.weightPPM)) / 1000000; - allocated += share; - } else { - // For the last author, allocate the remainder. - share = amount - allocated; - } - if (share > 0) { - dao.update(address(dao), author.authorAddress, uint(share)); - dao.registerMember(author.authorAddress); - } else if (dao.balanceOf(author.authorAddress) < uint(-share)) { - // Author has already lost some REP gained from this post. - // That means other DAO members have earned it for policing. - // We need to refund the difference here to ensure accurate bookkeeping - refund += share + int(dao.balanceOf(author.authorAddress)); - dao.update( - author.authorAddress, - address(dao), - dao.balanceOf(author.authorAddress) - ); - } else { - dao.update(author.authorAddress, address(dao), uint(-share)); - } - } - } - - function propagateReputation( - string memory postId, - int amount, - bool initialNegative, - uint depth - ) public returns (int refundToInbound) { - require( - msg.sender == address(dao) || msg.sender == address(this), - "Only DAO contract may call propagateReputation" - ); - if (depth >= depthLimit) { - return amount; - } - Post storage post = posts[postId]; - if (post.authors.length == 0) { - // We most likely got here via a reference to a post that hasn't been added yet. - // We support this scenario so that a reference graph can be imported one post at a time. - return amount; - } - // Propagate negative references first - for (uint i = 0; i < post.references.length; i++) { - if (post.references[i].weightPPM < 0) { - amount -= _handleReference( - postId, - post.references[i], - amount, - initialNegative, - depth - ); - } - } - // Now propagate positive references - for (uint i = 0; i < post.references.length; i++) { - if (post.references[i].weightPPM > 0) { - amount -= _handleReference( - postId, - post.references[i], - amount, - initialNegative, - depth - ); - } - } - if (amount > 0) { - _distributeAmongAuthors(post, amount); - post.reputation += uint(amount); - } else { - if (int(post.reputation) + amount >= 0) { - // Reduce the reputation of each author proportionately; - // If any author has insufficient reputation, refund the difference. - refundToInbound = _distributeAmongAuthors(post, amount); - post.reputation -= uint(-amount); - } else { - // If we applied the full amount, the post's reputation would decrease below zero. - refundToInbound = int(post.reputation) + amount; - refundToInbound += _distributeAmongAuthors( - post, - -int(post.reputation) - ); - post.reputation = 0; - } - } - } -} diff --git a/ethereum/scripts/deploy-dao.js b/ethereum/scripts/deploy-dao.js index a743e04..163e626 100644 --- a/ethereum/scripts/deploy-dao.js +++ b/ethereum/scripts/deploy-dao.js @@ -1,7 +1,7 @@ -const deployCoreContracts = require('./util/deploy-core-contracts'); +const deployDAOCoreContracts = require('./util/deploy-core-contracts'); async function main() { - await deployCoreContracts(); + await deployDAOCoreContracts(); } main().catch((error) => { diff --git a/ethereum/scripts/deploy.js b/ethereum/scripts/deploy.js index c14b6b4..03d4deb 100644 --- a/ethereum/scripts/deploy.js +++ b/ethereum/scripts/deploy.js @@ -1,13 +1,15 @@ require('dotenv').config(); +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'); -const deployCoreContracts = require('./util/deploy-core-contracts'); +const deployDAOCoreContracts = require('./util/deploy-core-contracts'); const { ROLLUP_INTERVAL } = process.env; async function main() { - await deployCoreContracts(); + await deployContract('GlobalForum'); + await deployDAOCoreContracts(); await deployDAOContract('Rollup', [ROLLUP_INTERVAL]); await deployDAOContract('Proposals'); await deployWorkContract('Work1'); diff --git a/ethereum/scripts/util/deploy-core-contracts.js b/ethereum/scripts/util/deploy-core-contracts.js index c001610..9a25711 100644 --- a/ethereum/scripts/util/deploy-core-contracts.js +++ b/ethereum/scripts/util/deploy-core-contracts.js @@ -4,15 +4,14 @@ const contractAddresses = require('../../contract-addresses.json'); const network = process.env.HARDHAT_NETWORK; -const deployCoreContracts = async () => { +const deployDAOCoreContracts = async () => { await deployContract('Reputation', [], true); - await deployContract('Forum', [], true); await deployContract('Bench', [], true); await deployContract('DAO', [ contractAddresses[network].Reputation, - contractAddresses[network].Forum, + contractAddresses[network].GlobalForum, contractAddresses[network].Bench, ], true); }; -module.exports = deployCoreContracts; +module.exports = deployDAOCoreContracts; diff --git a/ethereum/scripts/util/deploy-work-contract.js b/ethereum/scripts/util/deploy-work-contract.js index fbdf458..19e4b4e 100644 --- a/ethereum/scripts/util/deploy-work-contract.js +++ b/ethereum/scripts/util/deploy-work-contract.js @@ -13,6 +13,7 @@ const deployWorkContract = async (name) => { await deployContract(name, [ contractAddresses[network].DAO, + contractAddresses[network].GlobalForum, contractAddresses[network].Proposals, price]); }; diff --git a/ethereum/test/Forum.js b/ethereum/test/Forum.js index 516422d..0578264 100644 --- a/ethereum/test/Forum.js +++ b/ethereum/test/Forum.js @@ -8,16 +8,18 @@ const deployDAO = require('./util/deploy-dao'); describe('Forum', () => { async function deploy() { - const [account1, account2, account3] = await ethers.getSigners(); - const { dao } = await deployDAO(); + const [account1, account2, account3, account4] = await ethers.getSigners(); + const { dao, forum } = await deployDAO(); return { - dao, account1, account2, account3, + dao, forum, account1, account2, account3, account4, }; } let dao; + let forum; let account1; let account2; let account3; + let account4; const POOL_DURATION = 3600; // 1 hour const POOL_FEE = 100; const emptyCallbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []); @@ -39,7 +41,7 @@ describe('Forum', () => { { value: fee ?? POOL_FEE }, ); - const addPost = (author, postId, references) => dao.addPost([{ + const addPost = (author, postId, references) => forum.addPost([{ weightPPM: 1000000, authorAddress: author, }], postId, references); @@ -47,49 +49,43 @@ describe('Forum', () => { describe('Post', () => { beforeEach(async () => { ({ - dao, account1, account2, account3, + dao, forum, account1, account2, account3, account4, } = await loadFixture(deploy)); }); it('should be able to add a post', async () => { const postId = 'some-id'; - await expect(addPost(account1, postId, [])).to.emit(dao, 'PostAdded').withArgs('some-id'); - const post = await dao.posts(postId); + await expect(addPost(account1, postId, [])).to.emit(forum, 'PostAdded').withArgs('some-id'); + const post = await forum.getPost(postId); expect(post.sender).to.equal(account1); - expect(post.id).to.equal(postId); - const postAuthors = await dao.getPostAuthors(postId); - expect(postAuthors).to.have.length(1); - expect(postAuthors[0].weightPPM).to.equal(1000000); - expect(postAuthors[0].authorAddress).to.equal(account1); + expect(post.authors).to.have.length(1); + expect(post.authors[0].weightPPM).to.equal(1000000); + expect(post.authors[0].authorAddress).to.equal(account1); }); it('should be able to add a post on behalf of another account', async () => { const postId = 'some-id'; await addPost(account2, postId, []); - const post = await dao.posts(postId); + const post = await forum.getPost(postId); expect(post.sender).to.equal(account1); - expect(post.id).to.equal(postId); - const postAuthors = await dao.getPostAuthors(postId); - expect(postAuthors).to.have.length(1); - expect(postAuthors[0].weightPPM).to.equal(1000000); - expect(postAuthors[0].authorAddress).to.equal(account2); + expect(post.authors).to.have.length(1); + expect(post.authors[0].weightPPM).to.equal(1000000); + expect(post.authors[0].authorAddress).to.equal(account2); }); it('should be able to add a post with multiple authors', async () => { const postId = 'some-id'; - await expect(dao.addPost([ + await expect(forum.addPost([ { weightPPM: 500000, authorAddress: account1 }, { weightPPM: 500000, authorAddress: account2 }, - ], postId, [])).to.emit(dao, 'PostAdded').withArgs('some-id'); - const post = await dao.posts(postId); + ], postId, [])).to.emit(forum, 'PostAdded').withArgs('some-id'); + const post = await forum.getPost(postId); expect(post.sender).to.equal(account1); - expect(post.id).to.equal(postId); - const postAuthors = await dao.getPostAuthors(postId); - expect(postAuthors).to.have.length(2); - expect(postAuthors[0].weightPPM).to.equal(500000); - expect(postAuthors[0].authorAddress).to.equal(account1); - expect(postAuthors[1].weightPPM).to.equal(500000); - expect(postAuthors[1].authorAddress).to.equal(account2); + expect(post.authors).to.have.length(2); + expect(post.authors[0].weightPPM).to.equal(500000); + expect(post.authors[0].authorAddress).to.equal(account1); + expect(post.authors[1].weightPPM).to.equal(500000); + expect(post.authors[1].authorAddress).to.equal(account2); await initiateValidationPool({ postId: 'some-id' }); await time.increase(POOL_DURATION + 1); await dao.evaluateOutcome(0); @@ -99,7 +95,7 @@ describe('Forum', () => { it('should not be able to add a post with total author weight < 100%', async () => { const postId = 'some-id'; - await expect(dao.addPost([ + await expect(forum.addPost([ { weightPPM: 500000, authorAddress: account1 }, { weightPPM: 400000, authorAddress: account2 }, ], postId, [])).to.be.rejectedWith('Author weights must sum to 1000000'); @@ -107,7 +103,7 @@ describe('Forum', () => { it('should not be able to add a post with total author weight > 100%', async () => { const postId = 'some-id'; - await expect(dao.addPost([ + await expect(forum.addPost([ { weightPPM: 500000, authorAddress: account1 }, { weightPPM: 600000, authorAddress: account2 }, ], postId, [])).to.be.rejectedWith('Author weights must sum to 1000000'); @@ -126,13 +122,10 @@ describe('Forum', () => { it('should be able to leach reputation via references', async () => { await addPost(account1, 'content-id', []); - 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('content-id')).reputation).to.equal(100); await addPost(account2, 'second-content-id', [{ weightPPM: -500000, 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.props.postId).to.equal('second-content-id'); @@ -140,8 +133,6 @@ describe('Forum', () => { await dao.evaluateOutcome(1); expect(await dao.balanceOf(account1)).to.equal(50); expect(await dao.balanceOf(account2)).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 references', async () => { @@ -221,7 +212,6 @@ describe('Forum', () => { }); it('should limit effects of negative references on prior positive references', async () => { - console.log('First post'); await addPost(account1, 'content-id', []); await initiateValidationPool({ postId: 'content-id' }); await dao.evaluateOutcome(0); @@ -263,21 +253,15 @@ describe('Forum', () => { it('should enforce depth limit', async () => { await addPost(account1, 'content-id-1', []); - await addPost(account1, 'content-id-2', [{ weightPPM: 1000000, targetPostId: 'content-id-1' }]); - await addPost(account1, 'content-id-3', [{ weightPPM: 1000000, targetPostId: 'content-id-2' }]); - await addPost(account1, 'content-id-4', [{ weightPPM: 1000000, targetPostId: 'content-id-3' }]); + await addPost(account2, 'content-id-2', [{ weightPPM: 1000000, targetPostId: 'content-id-1' }]); + await addPost(account3, 'content-id-3', [{ weightPPM: 1000000, targetPostId: 'content-id-2' }]); + await addPost(account4, 'content-id-4', [{ weightPPM: 1000000, targetPostId: 'content-id-3' }]); await initiateValidationPool({ postId: 'content-id-4' }); await dao.evaluateOutcome(0); - const posts = await Promise.all([ - 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); - expect(posts[2].reputation).to.equal(0); - expect(posts[3].reputation).to.equal(0); + expect(await dao.balanceOf(account1)).to.equal(0); + expect(await dao.balanceOf(account2)).to.equal(100); + expect(await dao.balanceOf(account3)).to.equal(0); + expect(await dao.balanceOf(account4)).to.equal(0); }); it('should be able to incinerate reputation', async () => { @@ -290,7 +274,6 @@ describe('Forum', () => { await initiateValidationPool({ postId: 'content-id-1' }); expect(await dao.totalSupply()).to.equal(100); await dao.evaluateOutcome(0); - expect((await dao.posts('content-id-1')).reputation).to.equal(50); expect(await dao.totalSupply()).to.equal(50); }); @@ -301,7 +284,6 @@ describe('Forum', () => { await dao.evaluateOutcome(0); expect(await dao.balanceOf(account1)).to.equal(100); expect(await dao.totalSupply()).to.equal(100); - expect((await dao.posts('content-id')).reputation).to.equal(100); await addPost(account2, 'second-content-id', []); await initiateValidationPool({ postId: 'second-content-id' }); @@ -310,8 +292,6 @@ describe('Forum', () => { 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('content-id')).reputation).to.equal(100); - expect((await dao.posts('second-content-id')).reputation).to.equal(100); // account1 stakes and loses await initiateValidationPool({ postId: 'second-content-id' }); @@ -322,8 +302,6 @@ 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('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 () => { @@ -336,9 +314,6 @@ describe('Forum', () => { 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('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 () => { @@ -351,9 +326,6 @@ describe('Forum', () => { 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('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 () => { @@ -366,15 +338,12 @@ describe('Forum', () => { 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('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); }); }); describe('negative reference of a post with multiple authors', async () => { beforeEach(async () => { - await dao.addPost([ + await forum.addPost([ { weightPPM: 500000, authorAddress: account1 }, { weightPPM: 500000, authorAddress: account2 }, ], 'content-id', []); @@ -383,7 +352,6 @@ describe('Forum', () => { expect(await dao.balanceOf(account1)).to.equal(50); expect(await dao.balanceOf(account2)).to.equal(50); expect(await dao.totalSupply()).to.equal(100); - expect((await dao.posts('content-id')).reputation).to.equal(100); // account1 stakes and loses await initiateValidationPool({ postId: 'content-id' }); @@ -394,7 +362,6 @@ describe('Forum', () => { expect(await dao.balanceOf(account1)).to.equal(25); expect(await dao.balanceOf(account2)).to.equal(175); expect(await dao.totalSupply()).to.equal(200); - expect((await dao.posts('content-id')).reputation).to.equal(100); }); it('author and post rep can be completely destroyed', async () => { @@ -404,11 +371,9 @@ describe('Forum', () => { await time.increase(POOL_DURATION + 1); await dao.evaluateOutcome(2); expect(await dao.balanceOf(account1)).to.equal(0); - expect(await dao.balanceOf(account2)).to.equal(125); - expect(await dao.balanceOf(account3)).to.equal(475); + expect(await dao.balanceOf(account2)).to.equal(0); + expect(await dao.balanceOf(account3)).to.equal(600); expect(await dao.totalSupply()).to.equal(600); - expect((await dao.posts('content-id')).reputation).to.equal(0); - expect((await dao.posts('second-content-id')).reputation).to.equal(475); }); it('author rep can be destroyed while some post rep remains', async () => { @@ -421,8 +386,6 @@ describe('Forum', () => { expect(await dao.balanceOf(account1)).to.equal(0); expect(await dao.balanceOf(account2)).to.equal(140); expect(await dao.balanceOf(account3)).to.equal(130); - expect((await dao.posts('content-id')).reputation).to.equal(30); - expect((await dao.posts('second-content-id')).reputation).to.equal(130); }); }); }); diff --git a/ethereum/test/Onboarding.js b/ethereum/test/Onboarding.js index 0a5961a..9a2aa8c 100644 --- a/ethereum/test/Onboarding.js +++ b/ethereum/test/Onboarding.js @@ -13,13 +13,13 @@ describe('Onboarding', () => { // Contracts are deployed using the first signer/account by default const [account1, account2] = await ethers.getSigners(); - const { dao } = await deployDAO(); + const { dao, forum } = await deployDAO(); const Proposals = await ethers.getContractFactory('Proposals'); const proposals = await Proposals.deploy(dao.target); const Onboarding = await ethers.getContractFactory('Onboarding'); - const onboarding = await Onboarding.deploy(dao.target, proposals.target, PRICE); + const onboarding = await Onboarding.deploy(dao.target, forum.target, proposals.target, PRICE); - await dao.addPost([{ weightPPM: 1000000, authorAddress: account1 }], 'content-id', []); + await forum.addPost([{ weightPPM: 1000000, authorAddress: account1 }], 'content-id', []); const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []); await dao.initiateValidationPool( 'content-id', @@ -37,7 +37,7 @@ describe('Onboarding', () => { expect(await dao.balanceOf(account1)).to.equal(100); return { - dao, onboarding, account1, account2, + dao, forum, onboarding, account1, account2, }; } @@ -53,13 +53,14 @@ describe('Onboarding', () => { describe('Work approval/disapproval', () => { let dao; + let forum; let onboarding; let account1; let account2; beforeEach(async () => { ({ - dao, onboarding, account1, account2, + dao, forum, onboarding, account1, account2, } = await loadFixture(deploy)); await dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION); }); @@ -70,13 +71,11 @@ 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('evidence-content-id'); + const post = await forum.getPost('evidence-content-id'); expect(post.sender).to.equal(onboarding.target); - expect(post.id).to.equal('evidence-content-id'); - const postAuthors = await dao.getPostAuthors('evidence-content-id'); - expect(postAuthors).to.have.length(1); - expect(postAuthors[0].weightPPM).to.equal(1000000); - expect(postAuthors[0].authorAddress).to.equal(account1); + expect(post.authors).to.have.length(1); + expect(post.authors[0].weightPPM).to.equal(1000000); + expect(post.authors[0].authorAddress).to.equal(account1); const pool = await dao.validationPools(1); expect(pool.props.postId).to.equal('evidence-content-id'); expect(pool.props.fee).to.equal(PRICE * 0.9); @@ -114,7 +113,7 @@ describe('Onboarding', () => { describe('Onboarding followup', () => { it('resolving the first validation pool should trigger a second pool', async () => { const { - dao, onboarding, account2, + dao, forum, onboarding, account2, } = await loadFixture(deploy); await dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION); await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE }); @@ -122,14 +121,12 @@ describe('Onboarding', () => { await expect(onboarding.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1); 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('req-content-id'); + expect(await forum.postCount()).to.equal(3); + const post = await forum.getPost('req-content-id'); expect(post.sender).to.equal(onboarding.target); - expect(post.id).to.equal('req-content-id'); - const postAuthors = await dao.getPostAuthors('req-content-id'); - expect(postAuthors).to.have.length(1); - expect(postAuthors[0].weightPPM).to.equal(1000000); - expect(postAuthors[0].authorAddress).to.equal(account2); + expect(post.authors).to.have.length(1); + expect(post.authors[0].weightPPM).to.equal(1000000); + expect(post.authors[0].authorAddress).to.equal(account2); const pool = await dao.validationPools(2); expect(pool.props.postId).to.equal('req-content-id'); expect(pool.props.fee).to.equal(PRICE * 0.1); @@ -139,7 +136,7 @@ describe('Onboarding', () => { it('if the first validation pool is rejected it should not trigger a second pool', async () => { const { - dao, onboarding, account2, + dao, forum, onboarding, account2, } = await loadFixture(deploy); await dao.stakeAvailability(onboarding.target, 40, STAKE_DURATION); await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE }); @@ -148,7 +145,7 @@ describe('Onboarding', () => { await dao.stakeOnValidationPool(1, 60, false); await time.increase(86401); await expect(dao.evaluateOutcome(1)).not.to.emit(dao, 'ValidationPoolInitiated'); - expect(await dao.postCount()).to.equal(2); + expect(await forum.postCount()).to.equal(2); }); }); }); diff --git a/ethereum/test/Proposals.js b/ethereum/test/Proposals.js index 944f38d..116c353 100644 --- a/ethereum/test/Proposals.js +++ b/ethereum/test/Proposals.js @@ -12,12 +12,12 @@ describe('Proposal', () => { // Contracts are deployed using the first signer/account by default const [account1, account2] = await ethers.getSigners(); - const { dao } = await deployDAO(); + const { dao, forum } = await deployDAO(); const Proposals = await ethers.getContractFactory('Proposals'); const proposals = await Proposals.deploy(dao.target); - await dao.addPost([{ weightPPM: 1000000, authorAddress: account1 }], 'some-content-id', []); - await dao.addPost([{ weightPPM: 1000000, authorAddress: account2 }], 'some-other-content-id', []); + await forum.addPost([{ weightPPM: 1000000, authorAddress: account1 }], 'some-content-id', []); + await forum.addPost([{ weightPPM: 1000000, authorAddress: account2 }], 'some-other-content-id', []); const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []); await dao.initiateValidationPool( 'some-content-id', @@ -46,7 +46,7 @@ describe('Proposal', () => { await dao.evaluateOutcome(1); return { - dao, proposals, account1, account2, + dao, forum, proposals, account1, account2, }; } @@ -65,6 +65,7 @@ describe('Proposal', () => { describe('Attestation', () => { let dao; + let forum; let proposals; let account1; let account2; @@ -73,13 +74,14 @@ describe('Proposal', () => { beforeEach(async () => { ({ dao, + forum, proposals, account1, account2, } = await loadFixture(deploy)); const emptyCallbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []); - await dao.addPost([{ authorAddress: account1, weightPPM: 1000000 }], 'proposal-content-id', []); + await forum.addPost([{ authorAddress: account1, weightPPM: 1000000 }], 'proposal-content-id', []); 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); @@ -88,10 +90,10 @@ describe('Proposal', () => { }); it('Can submit a proposal', async () => { - const postAuthors = await dao.getPostAuthors('proposal-content-id'); - expect(postAuthors).to.have.length(1); - expect(postAuthors[0].weightPPM).to.equal(1000000); - expect(postAuthors[0].authorAddress).to.equal(account1); + const post = await forum.getPost('proposal-content-id'); + expect(post.authors).to.have.length(1); + expect(post.authors[0].weightPPM).to.equal(1000000); + expect(post.authors[0].authorAddress).to.equal(account1); }); it('Can attest for a proposal', async () => { diff --git a/ethereum/test/ValidationPools.js b/ethereum/test/ValidationPools.js index 3179069..35d4eba 100644 --- a/ethereum/test/ValidationPools.js +++ b/ethereum/test/ValidationPools.js @@ -9,12 +9,13 @@ const deployDAO = require('./util/deploy-dao'); describe('Validation Pools', () => { async function deploy() { const [account1, account2] = await ethers.getSigners(); - const { dao } = await deployDAO(); + const { dao, forum } = await deployDAO(); return { - dao, account1, account2, + dao, forum, account1, account2, }; } let dao; + let forum; let account1; let account2; const POOL_DURATION = 3600; // 1 hour @@ -40,9 +41,9 @@ describe('Validation Pools', () => { beforeEach(async () => { ({ - dao, account1, account2, + dao, forum, account1, account2, } = await loadFixture(deploy)); - await dao.addPost([{ weightPPM: 1000000, authorAddress: account1 }], 'content-id', []); + await forum.addPost([{ weightPPM: 1000000, authorAddress: account1 }], 'content-id', []); const init = () => initiateValidationPool({ fee: POOL_FEE }); await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(0); expect(await dao.validationPoolCount()).to.equal(1); @@ -206,7 +207,7 @@ describe('Validation Pools', () => { beforeEach(async () => { time.increase(POOL_DURATION + 1); await dao.evaluateOutcome(0); - await dao.addPost([{ weightPPM: 1000000, authorAddress: account2 }], 'content-id-2', []); + await forum.addPost([{ weightPPM: 1000000, authorAddress: 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); diff --git a/ethereum/test/Work1.js b/ethereum/test/Work1.js index a31ed15..3ffcac7 100644 --- a/ethereum/test/Work1.js +++ b/ethereum/test/Work1.js @@ -13,13 +13,13 @@ describe('Work1', () => { // Contracts are deployed using the first signer/account by default const [account1, account2] = await ethers.getSigners(); - const { dao } = await deployDAO(); + const { dao, forum } = await deployDAO(); const Proposals = await ethers.getContractFactory('Proposals'); const proposals = await Proposals.deploy(dao.target); const Work1 = await ethers.getContractFactory('Work1'); - const work1 = await Work1.deploy(dao.target, proposals.target, WORK1_PRICE); + const work1 = await Work1.deploy(dao.target, forum.target, proposals.target, WORK1_PRICE); - await dao.addPost([{ weightPPM: 1000000, authorAddress: account1 }], 'some-content-id', []); + await forum.addPost([{ weightPPM: 1000000, authorAddress: account1 }], 'some-content-id', []); const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []); await dao.initiateValidationPool( 'some-content-id', @@ -36,7 +36,7 @@ describe('Work1', () => { await dao.evaluateOutcome(0); return { - dao, work1, proposals, account1, account2, + dao, forum, work1, proposals, account1, account2, }; } @@ -185,13 +185,14 @@ describe('Work1', () => { describe('Work evidence and approval/disapproval', () => { let dao; + let forum; let work1; let account1; let account2; beforeEach(async () => { ({ - dao, work1, account1, account2, + dao, forum, work1, account1, account2, } = await loadFixture(deploy)); await dao.stakeAvailability(work1.target, 50, STAKE_DURATION); }); @@ -223,13 +224,11 @@ 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('evidence-content-id'); + const post = await forum.getPost('evidence-content-id'); expect(post.sender).to.equal(work1.target); - expect(post.id).to.equal('evidence-content-id'); - const postAuthors = await dao.getPostAuthors('evidence-content-id'); - expect(postAuthors).to.have.length(1); - expect(postAuthors[0].weightPPM).to.equal(1000000); - expect(postAuthors[0].authorAddress).to.equal(account1); + expect(post.authors).to.have.length(1); + expect(post.authors[0].weightPPM).to.equal(1000000); + expect(post.authors[0].authorAddress).to.equal(account1); const pool = await dao.validationPools(1); expect(pool.props.fee).to.equal(WORK1_PRICE); expect(pool.sender).to.equal(work1.target); diff --git a/ethereum/test/util/deploy-dao.js b/ethereum/test/util/deploy-dao.js index 01146bf..53c6d48 100644 --- a/ethereum/test/util/deploy-dao.js +++ b/ethereum/test/util/deploy-dao.js @@ -2,21 +2,21 @@ const { ethers } = require('hardhat'); const deployDAO = async () => { const Reputation = await ethers.getContractFactory('Reputation'); - const Forum = await ethers.getContractFactory('Forum'); const Bench = await ethers.getContractFactory('Bench'); const DAO = await ethers.getContractFactory('DAO'); + const GlobalForum = await ethers.getContractFactory('GlobalForum'); + const forum = await GlobalForum.deploy(); const reputation = await Reputation.deploy(); - const forum = await Forum.deploy(); const bench = await Bench.deploy(); const dao = await DAO.deploy( reputation.target, - forum.target, bench.target, + forum.target, ); return { + forum, dao, reputation, - forum, bench, }; };