UNPKG

@swaptoshi/governance-module

Version:

Klayr governance on-chain module

208 lines 10.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProposalQueue = void 0; const codec_1 = require("@klayr/codec"); const cryptography = require("@klayr/cryptography"); const utils = require("@klayr/utils"); const types_1 = require("../../types"); const queue_1 = require("../queue"); const base_1 = require("./base"); const utils_1 = require("../../utils"); const utils_2 = require("@swaptoshi/utils"); const utils_3 = require("@swaptoshi/utils"); const proposal_1 = require("../proposal"); const proposal_active_1 = require("../../events/proposal_active"); const proposal_quorum_checked_1 = require("../../events/proposal_quorum_checked"); const proposal_outcome_1 = require("../../events/proposal_outcome"); const schema_1 = require("../../schema"); const proposal_executed_1 = require("../../events/proposal_executed"); const proposal_voter_1 = require("../proposal_voter"); const casted_vote_1 = require("../casted_vote"); const payload_1 = require("../../utils/payload"); class ProposalQueue extends base_1.BaseInstance { constructor(stores, events, config, genesisConfig, moduleName, governableConfigRegistry, queue, height) { super(queue_1.ProposalQueueStore, stores, events, config, genesisConfig, moduleName, utils_2.bytes.numberToBytes(height)); this.start = []; this.quorum = []; this.ends = []; this.execute = []; Object.assign(this, utils.objects.cloneDeep(queue)); this.proposalStore = this.stores.get(proposal_1.ProposalStore); this.proposalVoterStore = this.stores.get(proposal_voter_1.ProposalVoterStore); this.castedVoteStore = this.stores.get(casted_vote_1.CastedVoteStore); this.governableConfigRegistry = governableConfigRegistry; } toJSON() { return utils.objects.cloneDeep(utils_3.object.serializer({ start: this.start, quorum: this.quorum, ends: this.ends, execute: this.execute, })); } toObject() { return utils.objects.cloneDeep({ start: this.start, quorum: this.quorum, ends: this.ends, execute: this.execute, }); } async executeQueue() { this._checkMutableDependencies(); for (const startedProposal of this.start) { await this._executeStartedProposal(startedProposal); } for (const proposalQuorumCheck of this.quorum) { await this._executeQuorumCheck(proposalQuorumCheck); } for (const endedProposal of this.ends) { await this._executeEndedProposal(endedProposal); } for (const executedProposal of this.execute) { await this._executeProposalOutcome(executedProposal); } } async _executeStartedProposal(proposalId) { this._checkMutableDependencies(); const proposal = await this.proposalStore.getMutableProposal(this.mutableContext, proposalId); await proposal.setStatus(types_1.ProposalStatus.ACTIVE); const events = this.events.get(proposal_active_1.ProposalActiveEvent); events.add(this.mutableContext.context, { proposalId, status: types_1.ProposalStatus.ACTIVE, }, [proposal.author]); } async _executeQuorumCheck(proposalId) { this._checkMutableDependencies(); const proposal = await this.proposalStore.getMutableProposal(this.mutableContext, proposalId); if (proposal.status !== types_1.ProposalStatus.ACTIVE) return; const stakingTokenId = this._getStakingTokenId(); const config = await this.config.getConfig(this.mutableContext.context); const totalSupplyStore = await this.tokenMethod.getTotalSupply(this.mutableContext.context); const index = totalSupplyStore.totalSupply.findIndex(supply => supply.tokenID.equals(stakingTokenId)); const treshold = (0, utils_1.parseBigintOrPercentage)(proposal.parameters.quorumTreshold, totalSupplyStore.totalSupply[index].totalSupply); let participant = BigInt(0); let status = types_1.ProposalStatus.ACTIVE; switch (proposal.parameters.quorumMode) { case types_1.QuorumMode.FOR_AGAINST_ABSTAIN: participant = proposal.turnout.for + proposal.turnout.against + proposal.turnout.abstain; break; case types_1.QuorumMode.FOR_AGAINST: participant = proposal.turnout.for + proposal.turnout.against; break; case types_1.QuorumMode.FOR: participant = proposal.turnout.for; break; default: throw new Error('unknown proposal.parameters.quorumMode'); } if (participant < treshold) { status = types_1.ProposalStatus.FAILED_QUORUM; await this.tokenMethod.unlock(this.mutableContext.context, proposal.author, this.moduleName, stakingTokenId, proposal.deposited); await this._removeProposalVoterCastedVote(proposalId); if (config.depositPoolAddress) { await this.tokenMethod.transfer(this.mutableContext.context, proposal.author, cryptography.address.getAddressFromKlayr32Address(config.depositPoolAddress), stakingTokenId, proposal.deposited); } else { await this.tokenMethod.burn(this.mutableContext.context, proposal.author, stakingTokenId, proposal.deposited); } } await proposal.setStatus(status); const events = this.events.get(proposal_quorum_checked_1.ProposalQuorumCheckedEvent); events.add(this.mutableContext.context, { proposalId, status, }, [proposal.author]); } async _executeEndedProposal(proposalId) { this._checkMutableDependencies(); const proposal = await this.proposalStore.getMutableProposal(this.mutableContext, proposalId); if (proposal.status !== types_1.ProposalStatus.ACTIVE) return; const stakingTokenId = this._getStakingTokenId(); await this.tokenMethod.unlock(this.mutableContext.context, proposal.author, this.moduleName, stakingTokenId, proposal.deposited); let status = types_1.ProposalStatus.ACTIVE; const totalSupplyStore = await this.tokenMethod.getTotalSupply(this.mutableContext.context); const index = totalSupplyStore.totalSupply.findIndex(supply => supply.tokenID.equals(stakingTokenId)); const { totalSupply } = totalSupplyStore.totalSupply[index]; if (proposal.voteSummary.for > proposal.voteSummary.against) { status = types_1.ProposalStatus.ACCEPTED; } if (proposal.parameters.enableTurnoutBias) { const participant = proposal.turnout.for + proposal.turnout.against + proposal.turnout.abstain; if (!(0, utils_1.isSatisfyTurnoutBias)(proposal.turnout.for, proposal.turnout.against, participant, totalSupply)) { status = types_1.ProposalStatus.REJECTED; } } await proposal.setStatus(status); await this._removeProposalVoterCastedVote(proposalId); const events = this.events.get(proposal_outcome_1.ProposalOutcomeEvent); events.add(this.mutableContext.context, { proposalId, status, turnoutBiasEnabled: proposal.parameters.enableTurnoutBias, boostingEnabled: proposal.parameters.enableBoosting, }, [proposal.author]); } async _executeProposalOutcome(proposalId) { this._checkMutableDependencies(); const proposal = await this.proposalStore.getMutableProposal(this.mutableContext, proposalId); if (proposal.status !== types_1.ProposalStatus.ACCEPTED) return; const events = this.events.get(proposal_executed_1.ProposalExecutedEvent); const actions = [...proposal.actions].sort((a, b) => { if (a.type === 'funding' && b.type !== 'funding') return -1; if (a.type !== 'funding' && b.type === 'funding') return 1; return 0; }); try { for (const action of actions) { if (action.type === 'funding') await this._executeFundingAction(action.payload); if (action.type === 'config') await this._executeConfigAction(action.payload); } await proposal.setStatus(types_1.ProposalStatus.EXECUTED); events.add(this.mutableContext.context, { proposalId, status: types_1.ProposalStatus.EXECUTED, }, [proposal.author]); } catch { await proposal.setStatus(types_1.ProposalStatus.EXECUTED_WITH_ERROR); events.add(this.mutableContext.context, { proposalId, status: types_1.ProposalStatus.EXECUTED_WITH_ERROR, }, [proposal.author]); } } async _executeFundingAction(encodedPayload) { this._checkMutableDependencies(); const config = await this.config.getConfig(this.mutableContext.context); const payload = codec_1.codec.decode(schema_1.fundingActionPayloadSchema, encodedPayload); await this.tokenMethod.transfer(this.mutableContext.context, cryptography.address.getAddressFromKlayr32Address(config.treasuryAddress), payload.receivingAddress, payload.tokenId, payload.fundingAmount); } async _executeConfigAction(encodedPayload) { this._checkMutableDependencies(); const payload = codec_1.codec.decode(schema_1.configActionPayloadSchema, encodedPayload); const targetConfig = this.governableConfigRegistry.get(payload.moduleName); const decodedValue = (0, payload_1.decodeConfigProposalValue)(targetConfig.schema, payload); await targetConfig.setConfigWithPath(this.mutableContext.context, payload.paramPath, decodedValue); } async _removeProposalVoterCastedVote(proposalId) { this._checkMutableDependencies(); const proposalVoters = await this.proposalVoterStore.getOrDefault(this.mutableContext.context, proposalId); for (const voter of proposalVoters.voters) { await this.castedVoteStore.removeCastedVoteByProposalId(this.mutableContext.context, voter, proposalId); } } _getStakingTokenId() { return Buffer.concat([Buffer.from(this.genesisConfig.chainID, 'hex'), Buffer.from('00000000', 'hex')]); } } exports.ProposalQueue = ProposalQueue; //# sourceMappingURL=queue.js.map