deploy core contracts separately due to size limit
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 34s
Details
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 34s
Details
This commit is contained in:
parent
5aee8c9314
commit
40c1fd43d7
|
@ -1,11 +1,14 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x87933c1e51FdF52C58ee54348a9372bbDeE9A8Dc",
|
||||
"Work1": "0x215078c5cf21ffB79Ee14Cf09156B94a11b7340f",
|
||||
"Onboarding": "0x3c2820D27e7470075d16856D7D555FD5011451Ec",
|
||||
"Proposals": "0xCd5881DB1aa6b86283a9c5660FaB65C989cf2721",
|
||||
"Rollup": "0x8611676563EBcd69dC52E5829bF2914A957398C3",
|
||||
"Work2": "0xC6BF1b68311e891D2BF41E4A3CB350a403831Ccd"
|
||||
"DAO": "0x3287061aDCeE36C1aae420a06E4a5EaE865Fe3ce",
|
||||
"Work1": "0x76Dfe9F47f06112a1b78960bf37d87CfbB6D6133",
|
||||
"Onboarding": "0xd2845aE812Ee42cF024fB4C55c052365792aBd78",
|
||||
"Proposals": "0x8688E736D0D72161db4D25f68EF7d0EE4856ba19",
|
||||
"Rollup": "0x8BDA04936887cF11263B87185E4D19e8158c6296",
|
||||
"Work2": "0xf15aCe29E5e3e4bb31FCddF2C65DF7C440449a57",
|
||||
"Reputation": "0xC0f00E5915F9abE6476858fD1961EAf79395ea64",
|
||||
"Forum": "0x3734B0944ea37694E85AEF60D5b256d19EDA04be",
|
||||
"Bench": "0x71cb20D63576a0Fa4F620a2E96C73F82848B09e1"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0x8e5bd58B2ca8910C5F9be8de847d6883B15c60d2",
|
||||
|
|
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
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
File diff suppressed because one or more lines are too long
|
@ -90,9 +90,9 @@ const generatePost = async (paper) => {
|
|||
throw new Error('Paper has no authors with id');
|
||||
}
|
||||
const firstAuthorWallet = new ethers.Wallet(authorsInfo[0].authorPrivKey);
|
||||
const eachAuthorWeightPercent = Math.floor(1000000 / authorsInfo.length);
|
||||
const eachAuthorWeightPPM = Math.floor(1000000 / authorsInfo.length);
|
||||
const authors = authorsInfo.map(({ authorAddress }) => ({
|
||||
weightPPM: eachAuthorWeightPercent,
|
||||
weightPPM: eachAuthorWeightPPM,
|
||||
authorAddress,
|
||||
}));
|
||||
// Make sure author weights sum to 100%
|
||||
|
@ -136,7 +136,7 @@ const importPaper = async (paper) => {
|
|||
console.log('references count:', paper.references.length);
|
||||
const { paperId } = paper;
|
||||
const references = paper.references.filter((x) => !!x.paperId);
|
||||
const eachCitationWeightPercent = Math.floor(PPM_TO_CITATIONS / references.length);
|
||||
const eachCitationWeightPPM = Math.floor(PPM_TO_CITATIONS / references.length);
|
||||
const citations = (await Promise.mapSeries(
|
||||
references,
|
||||
async (citedPaper) => {
|
||||
|
@ -147,7 +147,7 @@ const importPaper = async (paper) => {
|
|||
const citedPost = await generatePost(citedPaper);
|
||||
const citedPostHash = objectHash(citedPost);
|
||||
return {
|
||||
weightPPM: eachCitationWeightPercent,
|
||||
weightPPM: eachCitationWeightPPM,
|
||||
targetPostId: citedPostHash,
|
||||
};
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = {
|
||||
start: () => {},
|
||||
};
|
|
@ -1,99 +0,0 @@
|
|||
const { dao, proposals } = require('../util/contracts');
|
||||
const read = require('../util/forum/read');
|
||||
|
||||
const {
|
||||
START_PROPOSAL_ID,
|
||||
STOP_PROPOSAL_ID,
|
||||
ENABLE_STAKING,
|
||||
} = process.env;
|
||||
|
||||
let enableStaking = true;
|
||||
|
||||
// Subscribe to proposal events
|
||||
const start = async () => {
|
||||
if (ENABLE_STAKING === 'false') {
|
||||
console.log('STAKING DISABLED');
|
||||
enableStaking = false;
|
||||
} else {
|
||||
if (STOP_PROPOSAL_ID) {
|
||||
enableStaking = false;
|
||||
// Check for status
|
||||
const proposal = await proposals.proposals(STOP_PROPOSAL_ID);
|
||||
if (proposal.stage === BigInt(5)) {
|
||||
// Proposal is accepted
|
||||
enableStaking = false;
|
||||
console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal is accepted. Diabling staking.`);
|
||||
} else if (proposal.stage === BigInt(4)) {
|
||||
// Proposal is failed
|
||||
console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal is failed. No effect.`);
|
||||
} else {
|
||||
// Register a ProposalAccepted event handler.
|
||||
console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal is stage ${proposal.stage.toString()}. Registering listener.`);
|
||||
const proposalAcceptedHandler = (proposalIndex) => {
|
||||
if (proposalIndex === STOP_PROPOSAL_ID) {
|
||||
console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal is accepted. Disabling staking.`);
|
||||
enableStaking = false;
|
||||
proposals.off('ProposalAccepted', proposalAcceptedHandler);
|
||||
}
|
||||
};
|
||||
proposals.on('ProposalAccepted', proposalAcceptedHandler);
|
||||
}
|
||||
}
|
||||
if (START_PROPOSAL_ID) {
|
||||
enableStaking = false;
|
||||
// Check for status
|
||||
const proposal = await proposals.proposals(START_PROPOSAL_ID);
|
||||
if (proposal.stage === BigInt(5)) {
|
||||
// Proposal is accepted
|
||||
enableStaking = true;
|
||||
console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal is accepted. Enabling staking.`);
|
||||
} else if (proposal.stage === BigInt(4)) {
|
||||
// Proposal is failed
|
||||
console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal is failed. Disabling staking.`);
|
||||
} else {
|
||||
// Register a ProposalAccepted event handler.
|
||||
console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal is stage ${proposal.stage.toString()}. Registering listener.`);
|
||||
const proposalAcceptedHandler = (proposalIndex) => {
|
||||
if (proposalIndex === START_PROPOSAL_ID) {
|
||||
console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal is accepted. Enabling staking.`);
|
||||
enableStaking = true;
|
||||
proposals.off('ProposalAccepted', proposalAcceptedHandler);
|
||||
}
|
||||
};
|
||||
proposals.on('ProposalAccepted', proposalAcceptedHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('registering validation pool listener');
|
||||
dao.on('ValidationPoolInitiated', async (poolIndex) => {
|
||||
console.log('Validation Pool Initiated, index', poolIndex);
|
||||
|
||||
const pool = await dao.validationPools(poolIndex);
|
||||
console.log('postId:', pool.postId);
|
||||
|
||||
// Read post from database
|
||||
let post;
|
||||
try {
|
||||
post = await read(pool.postId);
|
||||
} catch (e) {
|
||||
// Post not found
|
||||
console.error(`error: post for validation pool ${poolIndex} not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('post.content:', post.content);
|
||||
|
||||
// We have the opportunity to stake for/against this validation pool.
|
||||
// To implement the legislative process of upgrading this protocol,
|
||||
// the execution of this code can be protected by a given proposal.
|
||||
// The code will only execute if the proposal has been accepted.
|
||||
if (!enableStaking) return;
|
||||
|
||||
console.log('considering staking');
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
start,
|
||||
};
|
|
@ -2,7 +2,7 @@ require('dotenv').config();
|
|||
|
||||
const api = require('./api');
|
||||
const matrixBot = require('./matrix-bot');
|
||||
const contractListeners = require('./contract-listeners');
|
||||
const topics = require('./topics');
|
||||
|
||||
const {
|
||||
ENABLE_API,
|
||||
|
@ -17,4 +17,4 @@ if (ENABLE_MATRIX !== 'false') {
|
|||
matrixBot.start();
|
||||
}
|
||||
|
||||
contractListeners.start();
|
||||
topics.start();
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
const proposals = require('./proposals');
|
||||
const proposalsNotifier = require('./proposals-notifier');
|
||||
const validationPools = require('./validation-pools');
|
||||
const work1 = require('./work1');
|
||||
const rollup = require('./rollup');
|
||||
|
||||
const start = () => {
|
||||
proposals.start();
|
||||
|
||||
proposalsNotifier.start();
|
||||
validationPools.start();
|
||||
|
||||
work1.start();
|
||||
rollup.start();
|
||||
};
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
const { getContractAddressByNetworkName } = require('../util/contract-config');
|
||||
const { registerDecider } = require('./validation-pools');
|
||||
|
||||
const {
|
||||
ETH_NETWORK,
|
||||
} = process.env;
|
||||
|
||||
const rollupAddress = getContractAddressByNetworkName(ETH_NETWORK, 'Rollup');
|
||||
|
||||
const start = async () => {
|
||||
console.log('registering validation pool decider for rollup');
|
||||
registerDecider((pool, post) => {
|
||||
// If this is not sent by the work1 contract, it's not of interest here.
|
||||
if (pool.sender !== rollupAddress) return false;
|
||||
|
||||
// TODO: to derive the expected content here, we need information from Matrix.
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
start,
|
||||
};
|
|
@ -0,0 +1,60 @@
|
|||
const Promise = require('bluebird');
|
||||
|
||||
const { dao } = require('../util/contracts');
|
||||
const read = require('../util/forum/read');
|
||||
const gateByProposal = require('../util/gate-by-proposal');
|
||||
|
||||
const {
|
||||
ENABLE_STAKING,
|
||||
} = process.env;
|
||||
|
||||
const deciders = [];
|
||||
|
||||
const registerDecider = (decider) => {
|
||||
deciders.push(decider);
|
||||
};
|
||||
|
||||
let enableStaking;
|
||||
|
||||
if (ENABLE_STAKING === 'false') {
|
||||
console.log('STAKING DISABLED');
|
||||
enableStaking = false;
|
||||
} else {
|
||||
gateByProposal(((enable) => {
|
||||
enableStaking = enable;
|
||||
}));
|
||||
}
|
||||
|
||||
const start = async () => {
|
||||
dao.on('ValidationPoolInitiated', async (poolIndex) => {
|
||||
console.log('Validation Pool Initiated, index', poolIndex);
|
||||
const pool = await dao.validationPools(poolIndex);
|
||||
// Read post from database
|
||||
let post;
|
||||
try {
|
||||
post = await read(pool.props.postId);
|
||||
} catch (e) {
|
||||
// Post not found
|
||||
console.error(`error: post for validation pool ${poolIndex} not found`);
|
||||
return;
|
||||
}
|
||||
console.log('postId:', pool.props.postId);
|
||||
console.log('post.content:', post.content);
|
||||
const results = await Promise.mapSeries(deciders, (decider) => decider(pool, post));
|
||||
const inFavor = results.some((x) => x === true);
|
||||
// We have the opportunity to stake for/against this validation pool.
|
||||
// To implement the legislative process of upgrading this protocol,
|
||||
// the execution of this code can be protected by a given proposal.
|
||||
// The code will only execute if the proposal has been accepted.
|
||||
if (!enableStaking) {
|
||||
return;
|
||||
}
|
||||
console.log(`WOULD STAKE ${inFavor ? 'in favor of' : 'against'} pool ${poolIndex}`);
|
||||
// TODO: Stake half of available reputation
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
start,
|
||||
registerDecider,
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
const { getContractAddressByNetworkName } = require('../util/contract-config');
|
||||
const { registerDecider } = require('./validation-pools');
|
||||
|
||||
const {
|
||||
ETH_NETWORK,
|
||||
} = process.env;
|
||||
|
||||
const work1Address = getContractAddressByNetworkName(ETH_NETWORK, 'Work1');
|
||||
|
||||
const start = async () => {
|
||||
console.log('registering validation pool decider for work1');
|
||||
registerDecider((pool, post) => {
|
||||
// If this is not sent by the work1 contract, it's not of interest here.
|
||||
if (pool.sender !== work1Address) return false;
|
||||
|
||||
const expectedContent = 'This is a work evidence post';
|
||||
const result = post.content.startsWith(expectedContent);
|
||||
|
||||
console.log(`Work evidence ${result ? 'matched' : 'did not match'} the expected content`);
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
start,
|
||||
};
|
|
@ -0,0 +1,61 @@
|
|||
const { proposals } = require('./contracts');
|
||||
|
||||
const {
|
||||
START_PROPOSAL_ID,
|
||||
STOP_PROPOSAL_ID,
|
||||
} = process.env;
|
||||
|
||||
const gateByProposal = async (enable) => {
|
||||
enable(true);
|
||||
|
||||
if (STOP_PROPOSAL_ID) {
|
||||
enable(false);
|
||||
// Check for status
|
||||
const proposal = await proposals.proposals(STOP_PROPOSAL_ID);
|
||||
if (proposal.stage === BigInt(5)) {
|
||||
// Proposal is accepted
|
||||
enable(false);
|
||||
console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal is accepted. Diabling staking.`);
|
||||
} else if (proposal.stage === BigInt(4)) {
|
||||
// Proposal is failed
|
||||
console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal is failed. No effect.`);
|
||||
} else {
|
||||
// Register a ProposalAccepted event handler.
|
||||
console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal is stage ${proposal.stage.toString()}. Registering listener.`);
|
||||
const proposalAcceptedHandler = (proposalIndex) => {
|
||||
if (proposalIndex === STOP_PROPOSAL_ID) {
|
||||
console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal is accepted. Disabling staking.`);
|
||||
enable(false);
|
||||
proposals.off('ProposalAccepted', proposalAcceptedHandler);
|
||||
}
|
||||
};
|
||||
proposals.on('ProposalAccepted', proposalAcceptedHandler);
|
||||
}
|
||||
}
|
||||
if (START_PROPOSAL_ID) {
|
||||
enable(false);
|
||||
// Check for status
|
||||
const proposal = await proposals.proposals(START_PROPOSAL_ID);
|
||||
if (proposal.stage === BigInt(5)) {
|
||||
// Proposal is accepted
|
||||
enable(true);
|
||||
console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal is accepted. Enabling staking.`);
|
||||
} else if (proposal.stage === BigInt(4)) {
|
||||
// Proposal is failed
|
||||
console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal is failed. Disabling staking.`);
|
||||
} else {
|
||||
// Register a ProposalAccepted event handler.
|
||||
console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal is stage ${proposal.stage.toString()}. Registering listener.`);
|
||||
const proposalAcceptedHandler = (proposalIndex) => {
|
||||
if (proposalIndex === START_PROPOSAL_ID) {
|
||||
console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal is accepted. Enabling staking.`);
|
||||
enable(true);
|
||||
proposals.off('ProposalAccepted', proposalAcceptedHandler);
|
||||
}
|
||||
};
|
||||
proposals.on('ProposalAccepted', proposalAcceptedHandler);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default gateByProposal;
|
|
@ -1,11 +1,14 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x87933c1e51FdF52C58ee54348a9372bbDeE9A8Dc",
|
||||
"Work1": "0x215078c5cf21ffB79Ee14Cf09156B94a11b7340f",
|
||||
"Onboarding": "0x3c2820D27e7470075d16856D7D555FD5011451Ec",
|
||||
"Proposals": "0xCd5881DB1aa6b86283a9c5660FaB65C989cf2721",
|
||||
"Rollup": "0x8611676563EBcd69dC52E5829bF2914A957398C3",
|
||||
"Work2": "0xC6BF1b68311e891D2BF41E4A3CB350a403831Ccd"
|
||||
"DAO": "0x3287061aDCeE36C1aae420a06E4a5EaE865Fe3ce",
|
||||
"Work1": "0x76Dfe9F47f06112a1b78960bf37d87CfbB6D6133",
|
||||
"Onboarding": "0xd2845aE812Ee42cF024fB4C55c052365792aBd78",
|
||||
"Proposals": "0x8688E736D0D72161db4D25f68EF7d0EE4856ba19",
|
||||
"Rollup": "0x8BDA04936887cF11263B87185E4D19e8158c6296",
|
||||
"Work2": "0xf15aCe29E5e3e4bb31FCddF2C65DF7C440449a57",
|
||||
"Reputation": "0xC0f00E5915F9abE6476858fD1961EAf79395ea64",
|
||||
"Forum": "0x3734B0944ea37694E85AEF60D5b256d19EDA04be",
|
||||
"Bench": "0x71cb20D63576a0Fa4F620a2E96C73F82848B09e1"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0x8e5bd58B2ca8910C5F9be8de847d6883B15c60d2",
|
||||
|
|
|
@ -60,7 +60,7 @@ contract Onboarding is Work, IOnValidate {
|
|||
uint,
|
||||
uint,
|
||||
bytes calldata callbackData
|
||||
) external returns (uint) {
|
||||
) external {
|
||||
require(
|
||||
msg.sender == address(dao),
|
||||
"onValidate may only be called by the DAO contract"
|
||||
|
@ -70,7 +70,7 @@ contract Onboarding is Work, IOnValidate {
|
|||
if (!votePasses || !quorumMet) {
|
||||
// refund the customer the remaining amount
|
||||
payable(request.customer).transfer(request.fee / 10);
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
// Make onboarding post
|
||||
Citation[] memory emptyCitations;
|
||||
|
@ -87,6 +87,5 @@ contract Onboarding is Work, IOnValidate {
|
|||
false,
|
||||
""
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,7 +153,7 @@ contract Proposals is DAOContract, IOnValidate {
|
|||
uint stakedFor,
|
||||
uint stakedAgainst,
|
||||
bytes calldata callbackData
|
||||
) external returns (uint) {
|
||||
) external {
|
||||
require(
|
||||
msg.sender == address(dao),
|
||||
"onValidate may only be called by the DAO contract"
|
||||
|
@ -177,7 +177,7 @@ contract Proposals is DAOContract, IOnValidate {
|
|||
proposal.stage = Stage.Failed;
|
||||
emit ProposalFailed(proposalIndex, "Quorum not met");
|
||||
proposal.remainingFee += fee;
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Participation threshold of 50%
|
||||
|
@ -238,7 +238,7 @@ contract Proposals is DAOContract, IOnValidate {
|
|||
} else if (proposal.stage == Stage.Referendum100) {
|
||||
initiateValidationPool(proposalIndex, 2, proposal.fee / 10);
|
||||
}
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/// External function that will advance a proposal to the referendum process
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// SPDX-License-Identifier: Unlicense
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "./Reputation.sol";
|
||||
import "./DAO.sol";
|
||||
import "./Forum.sol";
|
||||
import "../interfaces/IOnValidate.sol";
|
||||
|
||||
struct ValidationPoolStake {
|
||||
uint id;
|
||||
|
@ -20,45 +19,55 @@ struct ValidationPoolParams {
|
|||
bool redistributeLosingStakes;
|
||||
}
|
||||
|
||||
struct ValidationPool {
|
||||
uint id;
|
||||
struct ValidationPoolProps {
|
||||
string postId;
|
||||
address sender;
|
||||
uint minted;
|
||||
mapping(uint => ValidationPoolStake) stakes;
|
||||
uint stakeCount;
|
||||
ValidationPoolParams params;
|
||||
uint fee;
|
||||
uint minted;
|
||||
uint endTime;
|
||||
bool resolved;
|
||||
bool outcome;
|
||||
}
|
||||
|
||||
struct ValidationPool {
|
||||
uint id;
|
||||
address sender;
|
||||
mapping(uint => ValidationPoolStake) stakes;
|
||||
uint stakeCount;
|
||||
ValidationPoolParams params;
|
||||
ValidationPoolProps props;
|
||||
bool callbackOnValidate;
|
||||
bytes callbackData;
|
||||
}
|
||||
|
||||
contract ValidationPools is Reputation, Forum {
|
||||
contract Bench {
|
||||
mapping(uint => ValidationPool) public validationPools;
|
||||
uint public validationPoolCount;
|
||||
DAO dao;
|
||||
|
||||
uint constant minDuration = 1; // 1 second
|
||||
uint constant maxDuration = 365_000_000 days; // 1 million years
|
||||
uint[2] minQuorum = [1, 10];
|
||||
|
||||
event ValidationPoolInitiated(uint poolIndex);
|
||||
event ValidationPoolResolved(
|
||||
uint poolIndex,
|
||||
bool votePasses,
|
||||
bool quorumMet
|
||||
);
|
||||
function registerDAO(DAO dao_) external {
|
||||
require(
|
||||
address(dao) == address(0),
|
||||
"A DAO has already been registered"
|
||||
);
|
||||
dao = dao_;
|
||||
}
|
||||
|
||||
/// Internal function to register a stake for/against a validation pool
|
||||
function _stakeOnValidationPool(
|
||||
ValidationPool storage pool,
|
||||
/// Register a stake for/against a validation pool
|
||||
function stakeOnValidationPool(
|
||||
uint poolIndex,
|
||||
address sender,
|
||||
uint256 amount,
|
||||
bool inFavor
|
||||
) internal {
|
||||
require(block.timestamp <= pool.endTime, "Pool end time has passed");
|
||||
) external {
|
||||
ValidationPool storage pool = validationPools[poolIndex];
|
||||
require(
|
||||
block.timestamp <= pool.props.endTime,
|
||||
"Pool end time has passed"
|
||||
);
|
||||
// We don't call _update here; We defer that until evaluateOutcome.
|
||||
uint stakeIndex = pool.stakeCount++;
|
||||
ValidationPoolStake storage s = pool.stakes[stakeIndex];
|
||||
|
@ -68,33 +77,9 @@ contract ValidationPools is Reputation, Forum {
|
|||
s.id = stakeIndex;
|
||||
}
|
||||
|
||||
/// Accept reputation stakes toward a validation pool
|
||||
function stakeOnValidationPool(
|
||||
uint poolIndex,
|
||||
uint256 amount,
|
||||
bool inFavor
|
||||
) public {
|
||||
ValidationPool storage pool = validationPools[poolIndex];
|
||||
_stakeOnValidationPool(pool, msg.sender, amount, inFavor);
|
||||
}
|
||||
|
||||
/// Accept reputation stakes toward a validation pool
|
||||
function delegatedStakeOnValidationPool(
|
||||
uint poolIndex,
|
||||
address owner,
|
||||
uint256 amount,
|
||||
bool inFavor
|
||||
) public {
|
||||
ValidationPool storage pool = validationPools[poolIndex];
|
||||
if (allowance(owner, msg.sender) < amount) {
|
||||
amount = allowance(owner, msg.sender);
|
||||
}
|
||||
_spendAllowance(owner, msg.sender, amount);
|
||||
_stakeOnValidationPool(pool, owner, amount, inFavor);
|
||||
}
|
||||
|
||||
/// Accept fee to initiate a validation pool
|
||||
function initiateValidationPool(
|
||||
address sender,
|
||||
string calldata postId,
|
||||
uint duration,
|
||||
uint[2] calldata quorum, // [Numerator, Denominator]
|
||||
|
@ -104,6 +89,10 @@ contract ValidationPools is Reputation, Forum {
|
|||
bool callbackOnValidate,
|
||||
bytes calldata callbackData
|
||||
) external payable returns (uint poolIndex) {
|
||||
require(
|
||||
msg.sender == address(dao),
|
||||
"Only DAO contract may call initiateValidationPool"
|
||||
);
|
||||
require(msg.value > 0, "Fee is required to initiate validation pool");
|
||||
require(duration >= minDuration, "Duration is too short");
|
||||
require(duration <= maxDuration, "Duration is too long");
|
||||
|
@ -114,69 +103,77 @@ 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[postId];
|
||||
require(post.authors.length != 0, "Target post not found");
|
||||
poolIndex = validationPoolCount++;
|
||||
ValidationPool storage pool = validationPools[poolIndex];
|
||||
pool.sender = msg.sender;
|
||||
pool.postId = postId;
|
||||
pool.fee = msg.value;
|
||||
pool.id = poolIndex;
|
||||
pool.sender = sender;
|
||||
pool.props.postId = postId;
|
||||
pool.props.fee = msg.value;
|
||||
pool.props.endTime = block.timestamp + duration;
|
||||
pool.params.quorum = quorum;
|
||||
pool.params.winRatio = winRatio;
|
||||
pool.params.bindingPercent = bindingPercent;
|
||||
pool.params.redistributeLosingStakes = redistributeLosingStakes;
|
||||
pool.params.duration = duration;
|
||||
pool.endTime = block.timestamp + duration;
|
||||
pool.id = poolIndex;
|
||||
pool.callbackOnValidate = callbackOnValidate;
|
||||
pool.callbackData = callbackData;
|
||||
// We use our privilege as the DAO contract to mint reputation in proportion with the fee.
|
||||
// Here we assume a minting ratio of 1
|
||||
// TODO: Make minting ratio an adjustable parameter
|
||||
_mint(address(this), msg.value);
|
||||
pool.minted = msg.value;
|
||||
emit ValidationPoolInitiated(poolIndex);
|
||||
dao.mint(address(dao), msg.value);
|
||||
pool.props.minted = msg.value;
|
||||
dao.emitValidationPoolInitiated(poolIndex);
|
||||
}
|
||||
|
||||
/// Evaluate outcome of a validation pool
|
||||
function evaluateOutcome(uint poolIndex) public returns (bool votePasses) {
|
||||
require(
|
||||
msg.sender == address(dao),
|
||||
"Only DAO contract may call evaluateOutcome"
|
||||
);
|
||||
ValidationPool storage pool = validationPools[poolIndex];
|
||||
require(pool.resolved == false, "Pool is already resolved");
|
||||
require(pool.props.resolved == false, "Pool is already resolved");
|
||||
uint stakedFor;
|
||||
uint stakedAgainst;
|
||||
ValidationPoolStake storage s;
|
||||
for (uint i = 0; i < pool.stakeCount; i++) {
|
||||
s = pool.stakes[i];
|
||||
// Make sure the sender still has the required balance.
|
||||
// If not, automatically decrease the staked amount.
|
||||
if (dao.balanceOf(s.sender) < s.amount) {
|
||||
s.amount = dao.balanceOf(s.sender);
|
||||
}
|
||||
if (s.inFavor) {
|
||||
stakedFor += s.amount;
|
||||
} else {
|
||||
stakedAgainst += s.amount;
|
||||
}
|
||||
}
|
||||
stakedFor += pool.minted / 2;
|
||||
stakedAgainst += pool.minted / 2;
|
||||
if (pool.minted % 2 != 0) {
|
||||
stakedFor += pool.props.minted / 2;
|
||||
stakedAgainst += pool.props.minted / 2;
|
||||
if (pool.props.minted % 2 != 0) {
|
||||
stakedFor += 1;
|
||||
}
|
||||
// Special case for early evaluation if dao.totalSupply has been staked
|
||||
require(
|
||||
block.timestamp > pool.endTime ||
|
||||
stakedFor + stakedAgainst == totalSupply(),
|
||||
block.timestamp > pool.props.endTime ||
|
||||
stakedFor + stakedAgainst == dao.totalSupply(),
|
||||
"Pool end time has not yet arrived"
|
||||
);
|
||||
// Check that quorum is met
|
||||
if (
|
||||
pool.params.quorum[1] * (stakedFor + stakedAgainst) <=
|
||||
totalSupply() * pool.params.quorum[0]
|
||||
dao.totalSupply() * pool.params.quorum[0]
|
||||
) {
|
||||
// TODO: Refund fee
|
||||
// TODO: this could be made available for the sender to withdraw
|
||||
// payable(pool.sender).transfer(pool.fee);
|
||||
pool.resolved = true;
|
||||
emit ValidationPoolResolved(poolIndex, false, false);
|
||||
// payable(pool.sender).transfer(pool.props.fee);
|
||||
pool.props.resolved = true;
|
||||
dao.emitValidationPoolResolved(poolIndex, false, false);
|
||||
// Callback if requested
|
||||
if (pool.callbackOnValidate) {
|
||||
IOnValidate(pool.sender).onValidate(
|
||||
dao.onValidate(
|
||||
pool.sender,
|
||||
votePasses,
|
||||
false,
|
||||
stakedFor,
|
||||
|
@ -193,9 +190,9 @@ contract ValidationPools is Reputation, Forum {
|
|||
votePasses =
|
||||
stakedFor * pool.params.winRatio[1] >=
|
||||
(stakedFor + stakedAgainst) * pool.params.winRatio[0];
|
||||
pool.resolved = true;
|
||||
pool.outcome = votePasses;
|
||||
emit ValidationPoolResolved(poolIndex, votePasses, true);
|
||||
pool.props.resolved = true;
|
||||
pool.props.outcome = votePasses;
|
||||
dao.emitValidationPoolResolved(poolIndex, votePasses, true);
|
||||
|
||||
// Value of losing stakes should be distributed among winners, in proportion to their stakes
|
||||
// Only bindingPercent % should be redistributed
|
||||
|
@ -209,10 +206,10 @@ contract ValidationPools is Reputation, Forum {
|
|||
// Losing stake
|
||||
uint amount = (s.amount * pool.params.bindingPercent) / 100;
|
||||
if (pool.params.redistributeLosingStakes) {
|
||||
_update(s.sender, address(this), amount);
|
||||
dao.update(s.sender, address(dao), amount);
|
||||
totalRewards += amount;
|
||||
} else {
|
||||
_burn(s.sender, amount);
|
||||
dao.burn(s.sender, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,7 +218,7 @@ contract ValidationPools is Reputation, Forum {
|
|||
// If vote passes, reward the author as though they had staked the winning portion of the VP initial stake
|
||||
// Here we assume a stakeForAuthor ratio of 0.5
|
||||
// TODO: Make stakeForAuthor an adjustable parameter
|
||||
totalRewards += pool.minted / 2;
|
||||
totalRewards += pool.props.minted / 2;
|
||||
// Include the losign portion of the VP initial stake
|
||||
// Issue rewards to the winners
|
||||
for (uint i = 0; i < pool.stakeCount; i++) {
|
||||
|
@ -234,26 +231,24 @@ contract ValidationPools is Reputation, Forum {
|
|||
uint reward = (((totalRewards * s.amount) /
|
||||
amountFromWinners) * pool.params.bindingPercent) / 100;
|
||||
totalAllocated += reward;
|
||||
_update(address(this), s.sender, reward);
|
||||
dao.update(address(dao), s.sender, reward);
|
||||
}
|
||||
}
|
||||
// Due to rounding, there may be some excess REP. Award it to the author.
|
||||
uint remainder = totalRewards - totalAllocated;
|
||||
if (pool.minted % 2 != 0) {
|
||||
if (pool.props.minted % 2 != 0) {
|
||||
// We staked the odd remainder in favor of the post, on behalf of the author.
|
||||
remainder += 1;
|
||||
}
|
||||
|
||||
// Transfer REP to the forum instead of to the author directly
|
||||
_propagateReputation(
|
||||
pool.postId,
|
||||
int(pool.minted / 2 + remainder),
|
||||
false,
|
||||
0
|
||||
dao.propagateReputation(
|
||||
pool.props.postId,
|
||||
int(pool.props.minted / 2 + remainder)
|
||||
);
|
||||
} else {
|
||||
// If vote does not pass, divide the losing stake among the winners
|
||||
totalRewards += pool.minted;
|
||||
totalRewards += pool.props.minted;
|
||||
for (uint i = 0; i < pool.stakeCount; i++) {
|
||||
s = pool.stakes[i];
|
||||
if (
|
||||
|
@ -262,25 +257,21 @@ contract ValidationPools is Reputation, Forum {
|
|||
) {
|
||||
// Winning stake
|
||||
uint reward = (((totalRewards * s.amount) /
|
||||
(amountFromWinners - pool.minted / 2)) *
|
||||
(amountFromWinners - pool.props.minted / 2)) *
|
||||
pool.params.bindingPercent) / 100;
|
||||
totalAllocated += reward;
|
||||
_update(address(this), s.sender, reward);
|
||||
dao.update(address(dao), s.sender, reward);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Distribute fee proportionately among all reputation holders
|
||||
for (uint i = 0; i < memberCount; i++) {
|
||||
address member = members[i];
|
||||
uint share = (pool.fee * balanceOf(member)) / totalSupply();
|
||||
// TODO: For efficiency this could be modified to hold the funds for recipients to withdraw
|
||||
payable(member).transfer(share);
|
||||
}
|
||||
dao.distributeFeeAmongMembers{value: pool.props.fee}();
|
||||
|
||||
// Callback if requested
|
||||
if (pool.callbackOnValidate) {
|
||||
IOnValidate(pool.sender).onValidate(
|
||||
dao.onValidate(
|
||||
pool.sender,
|
||||
votePasses,
|
||||
true,
|
||||
stakedFor,
|
|
@ -3,24 +3,115 @@ pragma solidity ^0.8.24;
|
|||
|
||||
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
import "./Reputation.sol";
|
||||
import "./ValidationPools.sol";
|
||||
import "./Bench.sol";
|
||||
import "./Forum.sol";
|
||||
import "../interfaces/IAcceptAvailability.sol";
|
||||
import "../interfaces/IOnValidate.sol";
|
||||
|
||||
contract DAO is Reputation, Forum, ValidationPools {
|
||||
// constructor(DAO importFrom) {
|
||||
// if (address(importFrom) != address(0)) {
|
||||
// for (uint i = 0; i < importFrom.memberCount(); i++) {
|
||||
// members[i] = importFrom.members(i);
|
||||
// isMember[members[i]] = true;
|
||||
// _mint(members[i], importFrom.balanceOf(members[i]));
|
||||
// }
|
||||
// for (uint i = 0; i < importFrom.postCount(); i++) {
|
||||
// string memory postId = importFrom.postIds(i);
|
||||
// Post memory post = importFrom.posts(postId);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
import "hardhat/console.sol";
|
||||
|
||||
contract DAO {
|
||||
Reputation rep;
|
||||
Forum forum;
|
||||
Bench bench;
|
||||
mapping(uint => address) public members;
|
||||
uint public memberCount;
|
||||
mapping(address => bool) public isMember;
|
||||
|
||||
event ValidationPoolInitiated(uint poolIndex);
|
||||
event ValidationPoolResolved(
|
||||
uint poolIndex,
|
||||
bool votePasses,
|
||||
bool quorumMet
|
||||
);
|
||||
event PostAdded(string id);
|
||||
|
||||
constructor(Reputation reputation_, Forum forum_, Bench bench_) {
|
||||
rep = reputation_;
|
||||
forum = forum_;
|
||||
bench = bench_;
|
||||
rep.registerDAO(this);
|
||||
forum.registerDAO(this);
|
||||
bench.registerDAO(this);
|
||||
}
|
||||
|
||||
function emitValidationPoolInitiated(uint poolIndex) public {
|
||||
emit ValidationPoolInitiated(poolIndex);
|
||||
}
|
||||
|
||||
function emitValidationPoolResolved(
|
||||
uint poolIndex,
|
||||
bool votePasses,
|
||||
bool quorumMet
|
||||
) public {
|
||||
emit ValidationPoolResolved(poolIndex, votePasses, quorumMet);
|
||||
}
|
||||
|
||||
function emitPostAdded(string memory id) public {
|
||||
emit PostAdded(id);
|
||||
}
|
||||
|
||||
function update(address from, address to, uint256 value) public {
|
||||
require(
|
||||
msg.sender == address(forum) || msg.sender == address(bench),
|
||||
"Only DAO core contracts may call update"
|
||||
);
|
||||
rep.update(from, to, value);
|
||||
}
|
||||
|
||||
function mint(address account, uint256 value) public {
|
||||
require(
|
||||
msg.sender == address(forum) || msg.sender == address(bench),
|
||||
"Only DAO core contracts may call mint"
|
||||
);
|
||||
rep.mint(account, value);
|
||||
}
|
||||
|
||||
function burn(address account, uint256 value) public {
|
||||
require(
|
||||
msg.sender == address(forum) || msg.sender == address(bench),
|
||||
"Only DAO core contracts may call burn"
|
||||
);
|
||||
rep.burn(account, value);
|
||||
}
|
||||
|
||||
function registerMember(address account) public {
|
||||
require(
|
||||
msg.sender == address(forum) || msg.sender == address(bench),
|
||||
"Only DAO core contracts may call registerMember"
|
||||
);
|
||||
if (!isMember[account]) {
|
||||
members[memberCount++] = account;
|
||||
isMember[account] = true;
|
||||
}
|
||||
}
|
||||
|
||||
function balanceOf(address account) public view returns (uint256) {
|
||||
return rep.balanceOf(account);
|
||||
}
|
||||
|
||||
function allowance(
|
||||
address owner,
|
||||
address spender
|
||||
) public view returns (uint256) {
|
||||
return rep.allowance(owner, spender);
|
||||
}
|
||||
|
||||
function spendAllowance(
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 value
|
||||
) public {
|
||||
return rep.spendAllowance(owner, spender, value);
|
||||
}
|
||||
|
||||
function forwardAllowance(
|
||||
address owner,
|
||||
address to,
|
||||
uint256 amount
|
||||
) public {
|
||||
return rep.forwardAllowance(owner, to, amount);
|
||||
}
|
||||
|
||||
/// Authorize a contract to transfer REP, and call that contract's acceptAvailability method
|
||||
function stakeAvailability(
|
||||
|
@ -28,10 +119,157 @@ contract DAO is Reputation, Forum, ValidationPools {
|
|||
uint256 value,
|
||||
uint duration
|
||||
) external returns (bool) {
|
||||
_approve(msg.sender, to, allowance(msg.sender, to) + value);
|
||||
rep.approve(msg.sender, to, allowance(msg.sender, to) + value);
|
||||
IAcceptAvailability(to).acceptAvailability(msg.sender, value, duration);
|
||||
return true;
|
||||
}
|
||||
|
||||
function totalSupply() public view returns (uint256) {
|
||||
return rep.totalSupply();
|
||||
}
|
||||
|
||||
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++) {
|
||||
address member = members[i];
|
||||
uint share;
|
||||
if (i < memberCount - 1) {
|
||||
share = (msg.value * balanceOf(member)) / totalSupply();
|
||||
allocated += share;
|
||||
} else {
|
||||
// Due to rounding, give the remainder to the last member
|
||||
share = msg.value - allocated;
|
||||
}
|
||||
// TODO: For efficiency this could be modified to hold the funds for recipients to withdraw
|
||||
payable(member).transfer(share);
|
||||
}
|
||||
}
|
||||
|
||||
function validationPools(
|
||||
uint poolIndex
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (
|
||||
uint id,
|
||||
address sender,
|
||||
uint stakeCount,
|
||||
ValidationPoolParams memory params,
|
||||
ValidationPoolProps memory props,
|
||||
bool callbackOnValidate,
|
||||
bytes memory callbackData
|
||||
)
|
||||
{
|
||||
return bench.validationPools(poolIndex);
|
||||
}
|
||||
|
||||
function validationPoolCount() public view returns (uint) {
|
||||
return bench.validationPoolCount();
|
||||
}
|
||||
|
||||
function initiateValidationPool(
|
||||
string calldata postId,
|
||||
uint duration,
|
||||
uint[2] calldata quorum, // [Numerator, Denominator]
|
||||
uint[2] calldata winRatio, // [Numerator, Denominator]
|
||||
uint bindingPercent,
|
||||
bool redistributeLosingStakes,
|
||||
bool callbackOnValidate,
|
||||
bytes calldata callbackData
|
||||
) external payable returns (uint) {
|
||||
return
|
||||
bench.initiateValidationPool{value: msg.value}(
|
||||
msg.sender,
|
||||
postId,
|
||||
duration,
|
||||
quorum,
|
||||
winRatio,
|
||||
bindingPercent,
|
||||
redistributeLosingStakes,
|
||||
callbackOnValidate,
|
||||
callbackData
|
||||
);
|
||||
}
|
||||
|
||||
function stakeOnValidationPool(
|
||||
uint poolIndex,
|
||||
uint256 amount,
|
||||
bool inFavor
|
||||
) public {
|
||||
require(
|
||||
balanceOf(msg.sender) >= amount,
|
||||
"Insufficient REP balance to cover stake"
|
||||
);
|
||||
// TODO: Encumber tokens
|
||||
bench.stakeOnValidationPool(poolIndex, msg.sender, amount, inFavor);
|
||||
}
|
||||
|
||||
/// Accept reputation stakes toward a validation pool
|
||||
function delegatedStakeOnValidationPool(
|
||||
uint poolIndex,
|
||||
address owner,
|
||||
uint256 amount,
|
||||
bool inFavor
|
||||
) public {
|
||||
if (allowance(owner, msg.sender) < amount) {
|
||||
amount = allowance(owner, msg.sender);
|
||||
}
|
||||
spendAllowance(owner, msg.sender, amount);
|
||||
bench.stakeOnValidationPool(poolIndex, owner, amount, inFavor);
|
||||
}
|
||||
|
||||
function evaluateOutcome(uint poolIndex) public returns (bool) {
|
||||
return bench.evaluateOutcome(poolIndex);
|
||||
}
|
||||
|
||||
function onValidate(
|
||||
address target,
|
||||
bool votePasses,
|
||||
bool quorumMet,
|
||||
uint stakedFor,
|
||||
uint stakedAgainst,
|
||||
bytes calldata callbackData
|
||||
) public {
|
||||
require(
|
||||
msg.sender == address(forum) || msg.sender == address(bench),
|
||||
"Only DAO core contracts may call onValidate"
|
||||
);
|
||||
IOnValidate(target).onValidate(
|
||||
votePasses,
|
||||
quorumMet,
|
||||
stakedFor,
|
||||
stakedAgainst,
|
||||
callbackData
|
||||
);
|
||||
}
|
||||
|
||||
function addPost(
|
||||
Author[] calldata authors,
|
||||
string calldata postId,
|
||||
Citation[] calldata citations
|
||||
) public {
|
||||
forum.addPost(msg.sender, authors, postId, citations);
|
||||
}
|
||||
|
||||
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 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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: Unlicense
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "./Reputation.sol";
|
||||
import "./DAO.sol";
|
||||
|
||||
struct Citation {
|
||||
int weightPPM;
|
||||
|
@ -22,11 +22,12 @@ struct Post {
|
|||
// TODO: timestamp
|
||||
}
|
||||
|
||||
contract Forum is Reputation {
|
||||
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);
|
||||
|
||||
|
@ -34,11 +35,24 @@ contract Forum is Reputation {
|
|||
// 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,
|
||||
Citation[] calldata citations
|
||||
) 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);
|
||||
|
@ -47,15 +61,15 @@ contract Forum is Reputation {
|
|||
post.authors.length == 0,
|
||||
"A post with this postId already exists"
|
||||
);
|
||||
post.sender = msg.sender;
|
||||
post.sender = sender;
|
||||
post.id = postId;
|
||||
uint authorTotalWeightPercent;
|
||||
uint authorTotalWeightPPM;
|
||||
for (uint i = 0; i < authors.length; i++) {
|
||||
authorTotalWeightPercent += authors[i].weightPPM;
|
||||
authorTotalWeightPPM += authors[i].weightPPM;
|
||||
post.authors.push(authors[i]);
|
||||
}
|
||||
require(
|
||||
authorTotalWeightPercent == 1000000,
|
||||
authorTotalWeightPPM == 1000000,
|
||||
"Author weights must sum to 1000000"
|
||||
);
|
||||
for (uint i = 0; i < citations.length; i++) {
|
||||
|
@ -84,7 +98,7 @@ contract Forum is Reputation {
|
|||
totalCitationWeightNeg >= -1000000,
|
||||
"Sum of negative citations must be >= -1000000"
|
||||
);
|
||||
emit PostAdded(postId);
|
||||
dao.emitPostAdded(postId);
|
||||
}
|
||||
|
||||
function getPostAuthors(
|
||||
|
@ -108,7 +122,7 @@ contract Forum is Reputation {
|
|||
outboundAmount >= 0,
|
||||
"Leaching from incinerator is forbidden"
|
||||
);
|
||||
_burn(address(this), uint(outboundAmount));
|
||||
dao.burn(address(dao), uint(outboundAmount));
|
||||
return outboundAmount;
|
||||
}
|
||||
int balanceToOutbound = _edgeBalances[postId][citation.targetPostId];
|
||||
|
@ -123,7 +137,7 @@ contract Forum is Reputation {
|
|||
: -balanceToOutbound;
|
||||
}
|
||||
}
|
||||
int refund = _propagateReputation(
|
||||
int refund = propagateReputation(
|
||||
citation.targetPostId,
|
||||
outboundAmount,
|
||||
initialNegative || (depth == 0 && citation.weightPPM < 0),
|
||||
|
@ -140,11 +154,7 @@ contract Forum is Reputation {
|
|||
int allocated;
|
||||
|
||||
for (uint i = 0; i < post.authors.length; i++) {
|
||||
address authorAddress = post.authors[i].authorAddress;
|
||||
if (!isMember[authorAddress]) {
|
||||
members[memberCount++] = authorAddress;
|
||||
isMember[authorAddress] = true;
|
||||
}
|
||||
dao.registerMember(post.authors[i].authorAddress);
|
||||
}
|
||||
for (uint i = 0; i < post.authors.length; i++) {
|
||||
Author memory author = post.authors[i];
|
||||
|
@ -157,33 +167,34 @@ contract Forum is Reputation {
|
|||
share = amount - allocated;
|
||||
}
|
||||
if (share > 0) {
|
||||
_update(address(this), author.authorAddress, uint(share));
|
||||
if (!isMember[author.authorAddress]) {
|
||||
members[memberCount++] = author.authorAddress;
|
||||
isMember[author.authorAddress] = true;
|
||||
}
|
||||
} else if (balanceOf(author.authorAddress) < uint(-share)) {
|
||||
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(balanceOf(author.authorAddress));
|
||||
_update(
|
||||
refund += share + int(dao.balanceOf(author.authorAddress));
|
||||
dao.update(
|
||||
author.authorAddress,
|
||||
address(this),
|
||||
balanceOf(author.authorAddress)
|
||||
address(dao),
|
||||
dao.balanceOf(author.authorAddress)
|
||||
);
|
||||
} else {
|
||||
_update(author.authorAddress, address(this), uint(-share));
|
||||
dao.update(author.authorAddress, address(dao), uint(-share));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _propagateReputation(
|
||||
function propagateReputation(
|
||||
string memory postId,
|
||||
int amount,
|
||||
bool initialNegative,
|
||||
uint depth
|
||||
) internal returns (int refundToInbound) {
|
||||
) public returns (int refundToInbound) {
|
||||
require(
|
||||
msg.sender == address(dao) || msg.sender == address(this),
|
||||
"Only DAO contract may call propagateReputation"
|
||||
);
|
||||
if (depth >= depthLimit) {
|
||||
return amount;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,36 @@
|
|||
pragma solidity ^0.8.24;
|
||||
|
||||
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
import "./DAO.sol";
|
||||
|
||||
contract Reputation is ERC20("Reputation", "REP") {
|
||||
mapping(uint => address) public members;
|
||||
uint public memberCount;
|
||||
mapping(address => bool) public isMember;
|
||||
DAO dao;
|
||||
|
||||
function registerDAO(DAO dao_) external {
|
||||
require(
|
||||
address(dao) == address(0),
|
||||
"A DAO has already been registered"
|
||||
);
|
||||
dao = dao_;
|
||||
}
|
||||
|
||||
function update(address from, address to, uint256 value) public {
|
||||
require(
|
||||
msg.sender == address(dao),
|
||||
"Only DAO contract may call update"
|
||||
);
|
||||
_update(from, to, value);
|
||||
}
|
||||
|
||||
function mint(address account, uint256 value) public {
|
||||
require(msg.sender == address(dao), "Only DAO contract may call mint");
|
||||
_mint(account, value);
|
||||
}
|
||||
|
||||
function burn(address account, uint256 value) public {
|
||||
require(msg.sender == address(dao), "Only DAO contract may call burn");
|
||||
_burn(account, value);
|
||||
}
|
||||
|
||||
function decimals() public pure override returns (uint8) {
|
||||
return 9;
|
||||
|
@ -30,7 +55,31 @@ contract Reputation is ERC20("Reputation", "REP") {
|
|||
address to,
|
||||
uint256 amount
|
||||
) public {
|
||||
require(
|
||||
msg.sender == address(dao),
|
||||
"Only DAO contract may call spendAllowance"
|
||||
);
|
||||
_spendAllowance(owner, msg.sender, amount);
|
||||
_approve(owner, to, allowance(owner, to) + amount);
|
||||
}
|
||||
|
||||
function spendAllowance(
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 value
|
||||
) public {
|
||||
require(
|
||||
msg.sender == address(dao),
|
||||
"Only DAO contract may call spendAllowance"
|
||||
);
|
||||
_spendAllowance(owner, spender, value);
|
||||
}
|
||||
|
||||
function approve(address owner, address spender, uint256 value) public {
|
||||
require(
|
||||
msg.sender == address(dao),
|
||||
"Only DAO contract may call update"
|
||||
);
|
||||
_approve(owner, spender, value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,5 +8,5 @@ interface IOnValidate {
|
|||
uint stakedFor,
|
||||
uint stakedAgainst,
|
||||
bytes calldata callbackData
|
||||
) external returns (uint);
|
||||
) external;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
const { ethers } = require('hardhat');
|
||||
const { execSync } = require('child_process');
|
||||
const { getContractAddressByNetworkName } = require('./contract-config');
|
||||
const readFromApi = require('./util/read-from-api');
|
||||
|
||||
const network = process.env.HARDHAT_NETWORK;
|
||||
let currentVersionProposalId;
|
||||
|
||||
let dao;
|
||||
let work1;
|
||||
|
@ -16,20 +14,6 @@ let posts;
|
|||
let proposalsContract;
|
||||
let proposals;
|
||||
|
||||
const getCurrentVersion = () => {
|
||||
const currentCommit = execSync('git rev-parse HEAD');
|
||||
return currentCommit.toString();
|
||||
};
|
||||
|
||||
const fetchCurrentVersionProposal = async () => {
|
||||
// const p = await proposalsContract.
|
||||
};
|
||||
|
||||
const getLatestVersion = () => {
|
||||
const latestVersion = 'TBD';
|
||||
return latestVersion;
|
||||
};
|
||||
|
||||
const fetchReputation = async () => {
|
||||
reputation = await dao.balanceOf(account);
|
||||
console.log(`reputation: ${reputation}`);
|
||||
|
@ -75,22 +59,6 @@ const fetchValidationPools = async () => {
|
|||
await Promise.all(promises);
|
||||
};
|
||||
|
||||
const fetchProposal = async (proposalIndex) => {
|
||||
const proposal = await proposalsContract.proposals(proposalIndex);
|
||||
proposals[proposalIndex] = proposal;
|
||||
};
|
||||
|
||||
const fetchProposals = async () => {
|
||||
const count = await proposalsContract.proposalCount();
|
||||
console.log(`proposal count: ${count}`);
|
||||
const promises = [];
|
||||
proposals = [];
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
promises.push(fetchProposal(i));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
};
|
||||
|
||||
const initialize = async () => {
|
||||
const getContract = (name) => ethers.getContractAt(
|
||||
name,
|
||||
|
@ -106,12 +74,11 @@ const initialize = async () => {
|
|||
posts = [];
|
||||
await fetchReputation();
|
||||
await fetchValidationPools();
|
||||
await fetchProposals();
|
||||
};
|
||||
|
||||
const poolIsActive = (pool) => {
|
||||
if (new Date() >= new Date(Number(pool.endTime) * 1000)) return false;
|
||||
if (pool.resolved) return false;
|
||||
if (new Date() >= new Date(Number(pool.props.endTime) * 1000)) return false;
|
||||
if (pool.props.resolved) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -138,8 +105,8 @@ const poolIsProposal = (pool) => pool.sender === getContractAddressByNetworkName
|
|||
|
||||
const getPoolStatus = (pool) => {
|
||||
if (poolIsActive(pool)) return 'Active';
|
||||
if (!pool.resolved) return 'Ready to Evaluate';
|
||||
if (pool.outcome) return 'Accepted';
|
||||
if (!pool.props.resolved) return 'Ready to Evaluate';
|
||||
if (pool.props.outcome) return 'Accepted';
|
||||
return 'Rejected';
|
||||
};
|
||||
|
||||
|
@ -179,8 +146,6 @@ const printPool = (pool) => {
|
|||
};
|
||||
|
||||
async function main() {
|
||||
console.log('Current version:', getCurrentVersion());
|
||||
|
||||
await initialize();
|
||||
|
||||
validationPools.forEach(printPool);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const deployContract = require('./util/deploy-contract');
|
||||
const deployCoreContracts = require('./util/deploy-core-contracts');
|
||||
|
||||
async function main() {
|
||||
await deployContract('DAO', [], true);
|
||||
await deployCoreContracts();
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
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');
|
||||
|
||||
async function main() {
|
||||
await deployContract('DAO', [], true);
|
||||
await deployCoreContracts();
|
||||
await deployDAOContract('Rollup');
|
||||
await deployDAOContract('Proposals');
|
||||
await deployWorkContract('Work1');
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
require('dotenv').config();
|
||||
const deployContract = require('./deploy-contract');
|
||||
const contractAddresses = require('../../contract-addresses.json');
|
||||
|
||||
const network = process.env.HARDHAT_NETWORK;
|
||||
|
||||
const deployCoreContracts = async () => {
|
||||
await deployContract('Reputation', [], true);
|
||||
await deployContract('Forum', [], true);
|
||||
await deployContract('Bench', [], true);
|
||||
await deployContract('DAO', [
|
||||
contractAddresses[network].Reputation,
|
||||
contractAddresses[network].Forum,
|
||||
contractAddresses[network].Bench,
|
||||
], true);
|
||||
};
|
||||
|
||||
module.exports = deployCoreContracts;
|
|
@ -4,12 +4,12 @@ const {
|
|||
} = require('@nomicfoundation/hardhat-toolbox/network-helpers');
|
||||
const { expect } = require('chai');
|
||||
const { ethers } = require('hardhat');
|
||||
const deployDAO = require('./util/deploy-dao');
|
||||
|
||||
describe('Forum', () => {
|
||||
async function deploy() {
|
||||
const [account1, account2, account3] = await ethers.getSigners();
|
||||
const DAO = await ethers.getContractFactory('DAO');
|
||||
const dao = await DAO.deploy();
|
||||
const { dao } = await deployDAO();
|
||||
return {
|
||||
dao, account1, account2, account3,
|
||||
};
|
||||
|
@ -118,7 +118,7 @@ describe('Forum', () => {
|
|||
await addPost(account2, 'second-content-id', [{ weightPPM: 500000, targetPostId: 'content-id' }]);
|
||||
await initiateValidationPool({ postId: 'second-content-id' });
|
||||
const pool = await dao.validationPools(0);
|
||||
expect(pool.postId).to.equal('second-content-id');
|
||||
expect(pool.props.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);
|
||||
|
@ -135,7 +135,7 @@ describe('Forum', () => {
|
|||
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.postId).to.equal('second-content-id');
|
||||
expect(pool.props.postId).to.equal('second-content-id');
|
||||
await time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(1);
|
||||
expect(await dao.balanceOf(account1)).to.equal(50);
|
||||
|
@ -157,7 +157,7 @@ describe('Forum', () => {
|
|||
]);
|
||||
await initiateValidationPool({ postId: 'third-content-id' });
|
||||
const pool = await dao.validationPools(1);
|
||||
expect(pool.postId).to.equal('third-content-id');
|
||||
expect(pool.props.postId).to.equal('third-content-id');
|
||||
await time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(1);
|
||||
expect(await dao.balanceOf(account1)).to.equal(0);
|
||||
|
@ -193,7 +193,7 @@ describe('Forum', () => {
|
|||
]);
|
||||
await initiateValidationPool({ postId: 'second-content-id' });
|
||||
const pool = await dao.validationPools(0);
|
||||
expect(pool.postId).to.equal('second-content-id');
|
||||
expect(pool.props.postId).to.equal('second-content-id');
|
||||
await dao.evaluateOutcome(0);
|
||||
expect(await dao.balanceOf(account1)).to.equal(10);
|
||||
expect(await dao.balanceOf(account2)).to.equal(90);
|
||||
|
@ -388,7 +388,7 @@ describe('Forum', () => {
|
|||
// account1 stakes and loses
|
||||
await initiateValidationPool({ postId: 'content-id' });
|
||||
await dao.stakeOnValidationPool(1, 25, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(1, 60, false);
|
||||
await dao.connect(account2).stakeOnValidationPool(1, 50, false);
|
||||
await time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(1);
|
||||
expect(await dao.balanceOf(account1)).to.equal(25);
|
||||
|
|
|
@ -4,6 +4,7 @@ const {
|
|||
} = require('@nomicfoundation/hardhat-toolbox/network-helpers');
|
||||
const { expect } = require('chai');
|
||||
const { ethers } = require('hardhat');
|
||||
const deployDAO = require('./util/deploy-dao');
|
||||
|
||||
describe('Onboarding', () => {
|
||||
const PRICE = 100;
|
||||
|
@ -12,8 +13,7 @@ describe('Onboarding', () => {
|
|||
// Contracts are deployed using the first signer/account by default
|
||||
const [account1, account2] = await ethers.getSigners();
|
||||
|
||||
const DAO = await ethers.getContractFactory('DAO');
|
||||
const dao = await DAO.deploy();
|
||||
const { dao } = await deployDAO();
|
||||
const Proposals = await ethers.getContractFactory('Proposals');
|
||||
const proposals = await Proposals.deploy(dao.target);
|
||||
const Onboarding = await ethers.getContractFactory('Onboarding');
|
||||
|
@ -78,8 +78,8 @@ describe('Onboarding', () => {
|
|||
expect(postAuthors[0].weightPPM).to.equal(1000000);
|
||||
expect(postAuthors[0].authorAddress).to.equal(account1);
|
||||
const pool = await dao.validationPools(1);
|
||||
expect(pool.postId).to.equal('evidence-content-id');
|
||||
expect(pool.fee).to.equal(PRICE * 0.9);
|
||||
expect(pool.props.postId).to.equal('evidence-content-id');
|
||||
expect(pool.props.fee).to.equal(PRICE * 0.9);
|
||||
expect(pool.sender).to.equal(onboarding.target);
|
||||
});
|
||||
|
||||
|
@ -131,10 +131,10 @@ describe('Onboarding', () => {
|
|||
expect(postAuthors[0].weightPPM).to.equal(1000000);
|
||||
expect(postAuthors[0].authorAddress).to.equal(account2);
|
||||
const pool = await dao.validationPools(2);
|
||||
expect(pool.postId).to.equal('req-content-id');
|
||||
expect(pool.fee).to.equal(PRICE * 0.1);
|
||||
expect(pool.props.postId).to.equal('req-content-id');
|
||||
expect(pool.props.fee).to.equal(PRICE * 0.1);
|
||||
expect(pool.sender).to.equal(onboarding.target);
|
||||
expect(pool.fee);
|
||||
expect(pool.props.fee);
|
||||
});
|
||||
|
||||
it('if the first validation pool is rejected it should not trigger a second pool', async () => {
|
||||
|
|
|
@ -5,14 +5,14 @@ const {
|
|||
const { expect } = require('chai');
|
||||
const { ethers } = require('hardhat');
|
||||
const { beforeEach } = require('mocha');
|
||||
const deployDAO = require('./util/deploy-dao');
|
||||
|
||||
describe('Proposal', () => {
|
||||
async function deploy() {
|
||||
// Contracts are deployed using the first signer/account by default
|
||||
const [account1, account2] = await ethers.getSigners();
|
||||
|
||||
const DAO = await ethers.getContractFactory('DAO');
|
||||
const dao = await DAO.deploy();
|
||||
const { dao } = await deployDAO();
|
||||
const Proposals = await ethers.getContractFactory('Proposals');
|
||||
const proposals = await Proposals.deploy(dao.target);
|
||||
|
||||
|
@ -223,7 +223,7 @@ describe('Proposal', () => {
|
|||
|
||||
afterEach(async () => {
|
||||
const pool = await dao.validationPools(3);
|
||||
expect(pool.resolved).to.be.true;
|
||||
expect(pool.props.resolved).to.be.true;
|
||||
});
|
||||
|
||||
it('proposal dies if it fails to meet quorum', async () => {
|
||||
|
@ -309,7 +309,7 @@ describe('Proposal', () => {
|
|||
|
||||
afterEach(async () => {
|
||||
const pool = await dao.validationPools(4);
|
||||
expect(pool.resolved).to.be.true;
|
||||
expect(pool.props.resolved).to.be.true;
|
||||
});
|
||||
|
||||
it('proposal dies if it fails to meet quorum', async () => {
|
||||
|
|
|
@ -4,13 +4,15 @@ const {
|
|||
} = require('@nomicfoundation/hardhat-toolbox/network-helpers');
|
||||
const { expect } = require('chai');
|
||||
const { ethers } = require('hardhat');
|
||||
const deployDAO = require('./util/deploy-dao');
|
||||
|
||||
describe('Validation Pools', () => {
|
||||
async function deploy() {
|
||||
const [account1, account2] = await ethers.getSigners();
|
||||
const DAO = await ethers.getContractFactory('DAO');
|
||||
const dao = await DAO.deploy();
|
||||
return { dao, account1, account2 };
|
||||
const { dao } = await deployDAO();
|
||||
return {
|
||||
dao, account1, account2,
|
||||
};
|
||||
}
|
||||
let dao;
|
||||
let account1;
|
||||
|
@ -37,7 +39,9 @@ describe('Validation Pools', () => {
|
|||
);
|
||||
|
||||
beforeEach(async () => {
|
||||
({ dao, account1, account2 } = await loadFixture(deploy));
|
||||
({
|
||||
dao, account1, account2,
|
||||
} = await loadFixture(deploy));
|
||||
await dao.addPost([{ weightPPM: 1000000, authorAddress: account1 }], 'content-id', []);
|
||||
const init = () => initiateValidationPool({ fee: POOL_FEE });
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(0);
|
||||
|
@ -88,8 +92,8 @@ describe('Validation Pools', () => {
|
|||
const pool = await dao.validationPools(0);
|
||||
expect(pool).to.exist;
|
||||
expect(pool.params.duration).to.equal(POOL_DURATION);
|
||||
expect(pool.postId).to.equal('content-id');
|
||||
expect(pool.resolved).to.be.false;
|
||||
expect(pool.props.postId).to.equal('content-id');
|
||||
expect(pool.props.resolved).to.be.false;
|
||||
expect(pool.sender).to.equal(account1);
|
||||
});
|
||||
});
|
||||
|
@ -129,7 +133,17 @@ describe('Validation Pools', () => {
|
|||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
const pool = await dao.validationPools(1);
|
||||
expect(pool.outcome).to.be.false;
|
||||
expect(pool.props.outcome).to.be.false;
|
||||
});
|
||||
|
||||
it('should not be able to stake more REP than the sender owns', async () => {
|
||||
await expect(dao.stakeOnValidationPool(1, 200, true)).to.be.revertedWith('Insufficient REP balance to cover stake');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Delegated stake', () => {
|
||||
it('should stake the lesser of the allowed amount or the owner\'s remaining balance', async () => {
|
||||
// TODO: owner delegates stake and then loses rep
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -156,8 +170,8 @@ describe('Validation Pools', () => {
|
|||
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;
|
||||
expect(pool.props.resolved).to.be.true;
|
||||
expect(pool.props.outcome).to.be.true;
|
||||
});
|
||||
|
||||
it('should not be able to evaluate outcome more than once', async () => {
|
||||
|
|
|
@ -4,6 +4,7 @@ const {
|
|||
} = require('@nomicfoundation/hardhat-toolbox/network-helpers');
|
||||
const { expect } = require('chai');
|
||||
const { ethers } = require('hardhat');
|
||||
const deployDAO = require('./util/deploy-dao');
|
||||
|
||||
describe('Work1', () => {
|
||||
const WORK1_PRICE = 100;
|
||||
|
@ -12,8 +13,7 @@ describe('Work1', () => {
|
|||
// Contracts are deployed using the first signer/account by default
|
||||
const [account1, account2] = await ethers.getSigners();
|
||||
|
||||
const DAO = await ethers.getContractFactory('DAO');
|
||||
const dao = await DAO.deploy();
|
||||
const { dao } = await deployDAO();
|
||||
const Proposals = await ethers.getContractFactory('Proposals');
|
||||
const proposals = await Proposals.deploy(dao.target);
|
||||
const Work1 = await ethers.getContractFactory('Work1');
|
||||
|
@ -223,9 +223,9 @@ describe('Work1', () => {
|
|||
expect(postAuthors[0].weightPPM).to.equal(1000000);
|
||||
expect(postAuthors[0].authorAddress).to.equal(account1);
|
||||
const pool = await dao.validationPools(1);
|
||||
expect(pool.fee).to.equal(WORK1_PRICE);
|
||||
expect(pool.props.fee).to.equal(WORK1_PRICE);
|
||||
expect(pool.sender).to.equal(work1.target);
|
||||
expect(pool.postId).to.equal('evidence-content-id');
|
||||
expect(pool.props.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);
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
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 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,
|
||||
);
|
||||
return {
|
||||
dao,
|
||||
reputation,
|
||||
forum,
|
||||
bench,
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = deployDAO;
|
|
@ -1,11 +1,14 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x87933c1e51FdF52C58ee54348a9372bbDeE9A8Dc",
|
||||
"Work1": "0x215078c5cf21ffB79Ee14Cf09156B94a11b7340f",
|
||||
"Onboarding": "0x3c2820D27e7470075d16856D7D555FD5011451Ec",
|
||||
"Proposals": "0xCd5881DB1aa6b86283a9c5660FaB65C989cf2721",
|
||||
"Rollup": "0x8611676563EBcd69dC52E5829bF2914A957398C3",
|
||||
"Work2": "0xC6BF1b68311e891D2BF41E4A3CB350a403831Ccd"
|
||||
"DAO": "0x3287061aDCeE36C1aae420a06E4a5EaE865Fe3ce",
|
||||
"Work1": "0x76Dfe9F47f06112a1b78960bf37d87CfbB6D6133",
|
||||
"Onboarding": "0xd2845aE812Ee42cF024fB4C55c052365792aBd78",
|
||||
"Proposals": "0x8688E736D0D72161db4D25f68EF7d0EE4856ba19",
|
||||
"Rollup": "0x8BDA04936887cF11263B87185E4D19e8158c6296",
|
||||
"Work2": "0xf15aCe29E5e3e4bb31FCddF2C65DF7C440449a57",
|
||||
"Reputation": "0xC0f00E5915F9abE6476858fD1961EAf79395ea64",
|
||||
"Forum": "0x3734B0944ea37694E85AEF60D5b256d19EDA04be",
|
||||
"Bench": "0x71cb20D63576a0Fa4F620a2E96C73F82848B09e1"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0x8e5bd58B2ca8910C5F9be8de847d6883B15c60d2",
|
||||
|
|
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
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
File diff suppressed because one or more lines are too long
|
@ -211,9 +211,9 @@ function MainTabs() {
|
|||
{validationPools.filter((x) => !!x).map((pool) => (
|
||||
<tr key={pool.id}>
|
||||
<td>{pool.id.toString()}</td>
|
||||
<td>{pool.postId}</td>
|
||||
<td>{pool.props.postId}</td>
|
||||
<td>{getAddressName(chainId, pool.sender)}</td>
|
||||
<td>{pool.fee.toString()}</td>
|
||||
<td>{pool.props.fee.toString()}</td>
|
||||
<td>
|
||||
{pool.params.bindingPercent.toString()}
|
||||
%
|
||||
|
@ -222,11 +222,11 @@ function MainTabs() {
|
|||
<td>{`${pool.params.winRatio[0].toString()}/${pool.params.winRatio[1].toString()}`}</td>
|
||||
<td>{pool.params.redistributeLosingStakes.toString()}</td>
|
||||
<td>{pool.params.duration.toString()}</td>
|
||||
<td>{new Date(Number(pool.endTime) * 1000).toLocaleString()}</td>
|
||||
<td>{new Date(Number(pool.props.endTime) * 1000).toLocaleString()}</td>
|
||||
<td>{pool.stakeCount.toString()}</td>
|
||||
<td>{pool.status}</td>
|
||||
<td>
|
||||
{!pool.resolved && reputation > 0 && pool.timeRemaining > 0 && (
|
||||
{!pool.props.resolved && reputation > 0 && pool.timeRemaining > 0 && (
|
||||
<>
|
||||
<Button onClick={() => stakeHalfInFavor(pool.id)}>
|
||||
Stake 1/2 REP
|
||||
|
@ -238,7 +238,7 @@ function MainTabs() {
|
|||
{' '}
|
||||
</>
|
||||
)}
|
||||
{!pool.resolved && (pool.timeRemaining <= 0 || !reputation) && (
|
||||
{!pool.props.resolved && (pool.timeRemaining <= 0 || !reputation) && (
|
||||
<Button onClick={() => evaluateOutcome(pool.id)}>
|
||||
Evaluate Outcome
|
||||
</Button>
|
||||
|
|
|
@ -93,14 +93,14 @@ function MainContextProvider({ children }) {
|
|||
|
||||
const fetchValidationPool = useCallback(async (poolIndex) => {
|
||||
const getPoolStatus = (pool) => {
|
||||
if (pool.resolved) {
|
||||
return pool.outcome ? 'Accepted' : 'Rejected';
|
||||
if (pool.props.resolved) {
|
||||
return pool.props.outcome ? 'Accepted' : 'Rejected';
|
||||
}
|
||||
return pool.timeRemaining > 0 ? 'In Progress' : 'Ready to Evaluate';
|
||||
};
|
||||
const pool = await DAORef.current.methods.validationPools(poolIndex).call();
|
||||
pool.id = Number(pool.id);
|
||||
pool.timeRemaining = new Date(Number(pool.endTime) * 1000) - new Date();
|
||||
pool.timeRemaining = new Date(Number(pool.props.endTime) * 1000) - new Date();
|
||||
pool.status = getPoolStatus(pool);
|
||||
dispatchValidationPool({ type: 'update', item: pool });
|
||||
|
||||
|
|
Loading…
Reference in New Issue