diff --git a/backend/.env.example b/backend/.env.example index 015def8..8f904ef 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -10,4 +10,7 @@ MATRIX_PASSWORD= MATRIX_ACCESS_TOKEN= BOT_STORAGE_PATH="./data/bot-storage.json" BOT_CRYPTO_STORAGE_PATH="./data/bot-crypto" -BOT_INSTANCE_ID= \ No newline at end of file +BOT_INSTANCE_ID= +ENABLE_API= +ENABLE_MATRIX= +ENABLE_STAKING= \ No newline at end of file diff --git a/backend/src/contract-listeners/index.js b/backend/src/contract-listeners/index.js index 9e2f3c3..8b70e18 100644 --- a/backend/src/contract-listeners/index.js +++ b/backend/src/contract-listeners/index.js @@ -1,7 +1,13 @@ -const proposalsListener = require('./proposals'); +const proposals = require('./proposals'); +const validationPools = require('./validation-pools'); +const rollup = require('./rollup'); const start = () => { - proposalsListener.start(); + proposals.start(); + + validationPools.start(); + + rollup.start(); }; module.exports = { diff --git a/backend/src/contract-listeners/proposals.js b/backend/src/contract-listeners/proposals.js index 71173c9..5f623f5 100644 --- a/backend/src/contract-listeners/proposals.js +++ b/backend/src/contract-listeners/proposals.js @@ -1,5 +1,5 @@ const { proposals } = require('../util/contracts'); -const read = require('../api/read'); +const read = require('../util/forum/read'); const { sendNewProposalEvent } = require('../matrix-bot/outbound-queue'); // Subscribe to proposal events @@ -12,22 +12,25 @@ const start = () => { console.log('postId:', proposal.postId); // Read post from database + let post; try { - const post = await read(proposal.postId); - console.log('post.content:', post.content); - - // Send matrix room event - let message = `Proposal ${proposalIndex}\n\n${post.content}`; - if (post.embeddedData && Object.entries(post.embeddedData).length) { - message += `\n\n${JSON.stringify(post.embeddedData, null, 2)}`; - } - - // The outbound queue handles deduplication - sendNewProposalEvent(proposalIndex, message); + post = await read(proposal.postId); } catch (e) { - // Post for proposal not found + // Post for proposal not found console.error(`error: post for proposal ${proposalIndex} not found`); + return; } + + console.log('post.content:', post.content); + + // Send matrix room event + let message = `Proposal ${proposalIndex}\n\n${post.content}`; + if (post.embeddedData && Object.entries(post.embeddedData).length) { + message += `\n\n${JSON.stringify(post.embeddedData, null, 2)}`; + } + + // The outbound queue handles deduplication + sendNewProposalEvent(proposalIndex, message); }); }; diff --git a/backend/src/contract-listeners/rollup.js b/backend/src/contract-listeners/rollup.js new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/contract-listeners/validation-pools.js b/backend/src/contract-listeners/validation-pools.js new file mode 100644 index 0000000..67ea451 --- /dev/null +++ b/backend/src/contract-listeners/validation-pools.js @@ -0,0 +1,97 @@ +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') { + 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 accepted. Not starting.`); + } 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 = (event) => { + if (event.returnValues.proposalIndex === STOP_PROPOSAL_ID) { + console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal accepted. Stopping.`); + 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; + } else if (proposal.stage === BigInt(4)) { + // Proposal is failed + console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal is failed. Not starting.`); + } else { + // Register a ProposalAccepted event handler. + console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal is stage ${proposal.stage.toString()}. Registering listener.`); + const proposalAcceptedHandler = (event) => { + if (event.returnValues.proposalIndex === START_PROPOSAL_ID) { + console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal accepted. Starting.`); + 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, +}; diff --git a/backend/src/index.js b/backend/src/index.js index 8f45379..4a21b95 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -4,9 +4,16 @@ const api = require('./api'); const matrixBot = require('./matrix-bot'); const contractListeners = require('./contract-listeners'); -api.start(); +const { + ENABLE_API, + ENABLE_MATRIX, +} = process.env; -if (process.env.ENABLE_MATRIX !== 'false') { +if (ENABLE_API !== 'false') { + api.start(); +} + +if (ENABLE_MATRIX !== 'false') { matrixBot.start(); } diff --git a/ethereum/contracts/core/DAO.sol b/ethereum/contracts/core/DAO.sol index ab77c17..f1a95fe 100644 --- a/ethereum/contracts/core/DAO.sol +++ b/ethereum/contracts/core/DAO.sol @@ -8,6 +8,20 @@ import "./Forum.sol"; import "../interfaces/IAcceptAvailability.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); + // } + // } + // } + /// Authorize a contract to transfer REP, and call that contract's acceptAvailability method function stakeAvailability( address to,