UNPKG

@swaptoshi/governance-module

Version:

Klayr governance on-chain module

354 lines 22.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GovernanceModule = void 0; const klayr_framework_1 = require("klayr-framework"); const codec_1 = require("@klayr/codec"); const utils = require("@klayr/utils"); const validator = require("@klayr/validator"); const boost_vote_command_1 = require("./commands/boost_vote_command"); const create_proposal_command_1 = require("./commands/create_proposal_command"); const delegate_vote_command_1 = require("./commands/delegate_vote_command"); const revoke_delegated_vote_command_1 = require("./commands/revoke_delegated_vote_command"); const set_proposal_attributes_command_1 = require("./commands/set_proposal_attributes_command"); const vote_command_1 = require("./commands/vote_command"); const config_1 = require("./config"); const constants_1 = require("./constants"); const endpoint_1 = require("./endpoint"); const config_updated_1 = require("./events/config_updated"); const delegated_vote_revoked_1 = require("./events/delegated_vote_revoked"); const proposal_created_1 = require("./events/proposal_created"); const proposal_outcome_1 = require("./events/proposal_outcome"); const proposal_quorum_checked_1 = require("./events/proposal_quorum_checked"); const proposal_set_attributes_1 = require("./events/proposal_set_attributes"); const proposal_voted_1 = require("./events/proposal_voted"); const treasury_block_reward_tax_1 = require("./events/treasury_block_reward_tax"); const treasury_mint_1 = require("./events/treasury_mint"); const vote_boosted_1 = require("./events/vote_boosted"); const vote_delegated_1 = require("./events/vote_delegated"); const hooks_1 = require("./hooks"); const internal_method_1 = require("./internal_method"); const method_1 = require("./method"); const registry_1 = require("./registry"); const boosted_account_1 = require("./stores/boosted_account"); const delegated_vote_1 = require("./stores/delegated_vote"); const next_available_proposal_id_1 = require("./stores/next_available_proposal_id"); const proposal_1 = require("./stores/proposal"); const queue_1 = require("./stores/queue"); const context_1 = require("./stores/context"); const proposal_executed_1 = require("./events/proposal_executed"); const vote_changed_1 = require("./events/vote_changed"); const casted_vote_1 = require("./stores/casted_vote"); const vote_score_1 = require("./stores/vote_score"); const proposal_active_1 = require("./events/proposal_active"); const schema_1 = require("./schema"); const proposal_voter_1 = require("./stores/proposal_voter"); const utils_1 = require("@swaptoshi/utils"); const config_registered_1 = require("./events/config_registered"); const config_registry_1 = require("./stores/config_registry"); class GovernanceModule extends klayr_framework_1.Modules.BaseModule { constructor() { super(); this._config = new config_1.GovernanceGovernableConfig(this.name, constants_1.STORE_INDEX_MODULE_CONFIG); this.endpoint = new endpoint_1.GovernanceEndpoint(this.stores, this.offchainStores); this.method = new method_1.GovernanceMethod(this.stores, this.events); this.commands = [ new create_proposal_command_1.CreateProposalCommand(this.stores, this.events), new vote_command_1.VoteCommand(this.stores, this.events), new set_proposal_attributes_command_1.SetProposalAttributesCommand(this.stores, this.events), new boost_vote_command_1.BoostVoteCommand(this.stores, this.events), new delegate_vote_command_1.DelegateVoteCommand(this.stores, this.events), new revoke_delegated_vote_command_1.RevokeDelegatedVoteCommand(this.stores, this.events), ]; this._governableConfig = new registry_1.GovernableConfigRegistry(); this._internalMethod = new internal_method_1.GovernanceInternalMethod(this.stores, this.events); this.stores.register(config_1.GovernanceGovernableConfig, this._config); this.stores.register(proposal_1.ProposalStore, new proposal_1.ProposalStore(this.name, constants_1.STORE_INDEX_PROPOSAL, this.stores, this.events)); this.stores.register(queue_1.ProposalQueueStore, new queue_1.ProposalQueueStore(this.name, constants_1.STORE_INDEX_PROPOSAL_QUEUE, this.stores, this.events)); this.stores.register(boosted_account_1.BoostedAccountStore, new boosted_account_1.BoostedAccountStore(this.name, constants_1.STORE_INDEX_BOOSTED_ACCOUNT, this.stores, this.events)); this.stores.register(delegated_vote_1.DelegatedVoteStore, new delegated_vote_1.DelegatedVoteStore(this.name, constants_1.STORE_INDEX_DELEGTAED_VOTE, this.stores, this.events)); this.stores.register(next_available_proposal_id_1.NextAvailableProposalIdStore, new next_available_proposal_id_1.NextAvailableProposalIdStore(this.name, constants_1.STORE_INDEX_NEXT_AVAILABLE_PROPOSAL_ID)); this.stores.register(casted_vote_1.CastedVoteStore, new casted_vote_1.CastedVoteStore(this.name, constants_1.STORE_INDEX_CASTED_VOTE)); this.stores.register(vote_score_1.VoteScoreStore, new vote_score_1.VoteScoreStore(this.name, constants_1.STORE_INDEX_VOTE_SCORE)); this.stores.register(proposal_voter_1.ProposalVoterStore, new proposal_voter_1.ProposalVoterStore(this.name, constants_1.STORE_INDEX_PROPOSAL_VOTER)); this.stores.register(config_registry_1.ConfigRegistryStore, new config_registry_1.ConfigRegistryStore(this.name, constants_1.STORE_INDEX_CONFIG_REGISTRY)); this.events.register(config_updated_1.ConfigUpdatedEvent, new config_updated_1.ConfigUpdatedEvent(this.name)); this.events.register(delegated_vote_revoked_1.DelegatedVoteRevokedEvent, new delegated_vote_revoked_1.DelegatedVoteRevokedEvent(this.name)); this.events.register(proposal_active_1.ProposalActiveEvent, new proposal_active_1.ProposalActiveEvent(this.name)); this.events.register(proposal_created_1.ProposalCreatedEvent, new proposal_created_1.ProposalCreatedEvent(this.name)); this.events.register(proposal_outcome_1.ProposalOutcomeEvent, new proposal_outcome_1.ProposalOutcomeEvent(this.name)); this.events.register(proposal_executed_1.ProposalExecutedEvent, new proposal_executed_1.ProposalExecutedEvent(this.name)); this.events.register(proposal_quorum_checked_1.ProposalQuorumCheckedEvent, new proposal_quorum_checked_1.ProposalQuorumCheckedEvent(this.name)); this.events.register(proposal_set_attributes_1.ProposalSetAttributesEvent, new proposal_set_attributes_1.ProposalSetAttributesEvent(this.name)); this.events.register(proposal_voted_1.ProposalVotedEvent, new proposal_voted_1.ProposalVotedEvent(this.name)); this.events.register(vote_changed_1.VoteChangedEvent, new vote_changed_1.VoteChangedEvent(this.name)); this.events.register(treasury_block_reward_tax_1.TreasuryBlockRewardTaxEvent, new treasury_block_reward_tax_1.TreasuryBlockRewardTaxEvent(this.name)); this.events.register(treasury_mint_1.TreasuryMintEvent, new treasury_mint_1.TreasuryMintEvent(this.name)); this.events.register(vote_boosted_1.VoteBoostedEvent, new vote_boosted_1.VoteBoostedEvent(this.name)); this.events.register(vote_delegated_1.VoteDelegatedEvent, new vote_delegated_1.VoteDelegatedEvent(this.name)); this.events.register(config_registered_1.ConfigRegisteredEvent, new config_registered_1.ConfigRegisteredEvent(this.name)); this.method.init(this._governableConfig); this.endpoint.init(this._governableConfig); } get name() { return constants_1.MODULE_NAME_GOVERNANCE; } metadata() { return { ...this.baseMetadata(), endpoints: [ { name: this.endpoint.getConfig.name, request: schema_1.getConfigEndpointRequestSchema, response: schema_1.getConfigEndpointResponseSchema, }, { name: this.endpoint.getRegisteredGovernableConfig.name, request: schema_1.getRegisteredGovernableConfigEndpointRequestSchema, response: schema_1.getRegisteredGovernableConfigEndpointResponseSchema, }, { name: this.endpoint.getCastedVote.name, request: schema_1.getCastedVoteEndpointRequestSchema, response: schema_1.getCastedVoteEndpointResponseSchema, }, { name: this.endpoint.getBaseVoteScore.name, request: schema_1.getBaseVoteScoreEndpointRequestSchema, response: schema_1.getBaseVoteScoreEndpointResponseSchema, }, { name: this.endpoint.getProposal.name, request: schema_1.getProposalEndpointRequestSchema, response: schema_1.getProposalEndpointResponseSchema, }, { name: this.endpoint.getProposalQueue.name, request: schema_1.getProposalQueueEndpointRequestSchema, response: schema_1.getProposalQueueEndpointResponseSchema, }, { name: this.endpoint.getBoostedAccount.name, request: schema_1.getBoostedAccountEndpointRequestSchema, response: schema_1.getBoostedAccountEndpointResponseSchema, }, { name: this.endpoint.getDelegatedVote.name, request: schema_1.getDelegatedVoteEndpointRequestSchema, response: schema_1.getDelegatedVoteEndpointResponseSchema, }, { name: this.endpoint.getNextAvailableProposalId.name, request: schema_1.getNextAvailableProposalIdEndpointRequestSchema, response: schema_1.getNextAvailableProposalIdEndpointResponseSchema, }, ], assets: [ { version: 0, data: schema_1.governanceGenesisStoreSchema, }, ], }; } async init(args) { const config = utils.objects.mergeDeep({}, constants_1.defaultConfig, args.moduleConfig); if (config.governGovernanceConfig) { this.method.registerGovernableConfig(args, this.name, this._config); } else { this._config.init(args); } const proposalStore = this.stores.get(proposal_1.ProposalStore); const proposalQueueStore = this.stores.get(queue_1.ProposalQueueStore); const delegatedVoteStore = this.stores.get(delegated_vote_1.DelegatedVoteStore); const boostedAccountStore = this.stores.get(boosted_account_1.BoostedAccountStore); proposalStore.init(args.genesisConfig, this._config); proposalQueueStore.init(args.genesisConfig, this._config); delegatedVoteStore.init(args.genesisConfig, this._config); boostedAccountStore.init(args.genesisConfig, this._config); } addDependencies(dependencies) { const proposalStore = this.stores.get(proposal_1.ProposalStore); const proposalQueueStore = this.stores.get(queue_1.ProposalQueueStore); const delegatedVoteStore = this.stores.get(delegated_vote_1.DelegatedVoteStore); const boostedAccountStore = this.stores.get(boosted_account_1.BoostedAccountStore); proposalQueueStore.addDependencies({ ...dependencies, governableConfigRegistry: this._governableConfig }); proposalStore.addDependencies({ ...dependencies, governableConfigRegistry: this._governableConfig, internalMethod: this._internalMethod }); delegatedVoteStore.addDependencies({ ...dependencies, internalMethod: this._internalMethod }); boostedAccountStore.addDependencies({ ...dependencies, internalMethod: this._internalMethod }); this._feeMethod = dependencies.feeMethod; this._internalMethod.addDependencies(dependencies.tokenMethod); this._config.addDependencies(this._internalMethod); } async verifyTransaction(_context) { try { const ctx = (0, context_1.immutableTransactionHookGovernanceContext)(_context); const boostedAccount = await this.stores.get(boosted_account_1.BoostedAccountStore).getImmutableBoostedAccount(ctx); await boostedAccount.isValidUnstake(); await hooks_1.verifyMinimumFee.bind(this)(_context); await hooks_1.verifyBaseFee.bind(this)(_context); } catch (error) { return { status: klayr_framework_1.StateMachine.VerifyStatus.FAIL, error: new Error(error.message), }; } return { status: klayr_framework_1.StateMachine.VerifyStatus.OK }; } async beforeCommandExecute(_context) { const ctx = (0, context_1.immutableTransactionHookGovernanceContext)(_context); const boostedAccount = await this.stores.get(boosted_account_1.BoostedAccountStore).getImmutableBoostedAccount(ctx); await boostedAccount.isValidUnstake(); await hooks_1.verifyMinimumFee.bind(this)(_context); await hooks_1.verifyBaseFee.bind(this)(_context); await hooks_1.executeBaseFee.bind(this)(_context); } async afterCommandExecute(context) { await this._internalMethod.updateVoteScoreAfterStake(context); } async afterTransactionsExecute(context) { await this._internalMethod.addTreasuryReward(context); await this._internalMethod.executeQueuedProposal(context); } async beforeTransactionsExecute(context) { this._internalMethod.setModulePriorityStatus(context); } async initGenesisState(context) { const assetBytes = context.assets.getAsset(this.name); if (!assetBytes) { await this._initGovernableConfig(context); return; } const genesisStore = codec_1.codec.decode(schema_1.governanceGenesisStoreSchema, assetBytes); validator.validator.validate(schema_1.governanceGenesisStoreSchema, genesisStore); const boostedAccountStore = this.stores.get(boosted_account_1.BoostedAccountStore); const copiedBoostedAccountSubstore = [...genesisStore.boostedAccountSubstore]; copiedBoostedAccountSubstore.sort((a, b) => a.address.compare(b.address)); for (let i = 0; i < genesisStore.boostedAccountSubstore.length; i += 1) { const boostedAccountData = genesisStore.boostedAccountSubstore[i]; if (!boostedAccountData.address.equals(copiedBoostedAccountSubstore[i].address)) { throw new Error('boostedAccountSubstore must be sorted by address.'); } await boostedAccountStore.set(context, boostedAccountData.address, boostedAccountData); } const castedVoteStore = this.stores.get(casted_vote_1.CastedVoteStore); const copiedCastedVoteSubstore = [...genesisStore.castedVoteSubstore]; copiedCastedVoteSubstore.sort((a, b) => a.address.compare(b.address)); for (let i = 0; i < genesisStore.castedVoteSubstore.length; i += 1) { const castedVoteData = genesisStore.castedVoteSubstore[i]; if (!castedVoteData.address.equals(copiedCastedVoteSubstore[i].address)) { throw new Error('castedVoteSubstore must be sorted by address.'); } await castedVoteStore.set(context, castedVoteData.address, castedVoteData); } const delegatedVoteStore = this.stores.get(delegated_vote_1.DelegatedVoteStore); const copiedDelegatedVoteSubstore = [...genesisStore.delegatedVoteSubstore]; copiedDelegatedVoteSubstore.sort((a, b) => a.address.compare(b.address)); for (let i = 0; i < genesisStore.delegatedVoteSubstore.length; i += 1) { const delegatedVoteData = genesisStore.delegatedVoteSubstore[i]; if (!delegatedVoteData.address.equals(copiedDelegatedVoteSubstore[i].address)) { throw new Error('delegatedVoteSubstore must be sorted by address.'); } await delegatedVoteStore.set(context, delegatedVoteData.address, delegatedVoteData); } const nextAvailableProposalIdStore = this.stores.get(next_available_proposal_id_1.NextAvailableProposalIdStore); if (!genesisStore.nextAvailableProposalIdSubstore) throw new Error('nextAvailableProposalIdSubstore not present in governance genesis assets'); await nextAvailableProposalIdStore.set(context, Buffer.alloc(0), genesisStore.nextAvailableProposalIdSubstore); const proposalVoterStore = this.stores.get(proposal_voter_1.ProposalVoterStore); const copiedProposalVoterSubstore = [...genesisStore.proposalVoterSubstore]; copiedProposalVoterSubstore.sort((a, b) => a.proposalId - b.proposalId); for (let i = 0; i < genesisStore.proposalVoterSubstore.length; i += 1) { const proposalVoterData = genesisStore.proposalVoterSubstore[i]; if (proposalVoterData.proposalId !== copiedProposalVoterSubstore[i].proposalId) { throw new Error('proposalVoterSubstore must be sorted by proposalId.'); } await proposalVoterStore.set(context, utils_1.bytes.numberToBytes(proposalVoterData.proposalId), proposalVoterData); } const proposalStore = this.stores.get(proposal_1.ProposalStore); const copiedProposalSubstore = [...genesisStore.proposalSubstore]; copiedProposalSubstore.sort((a, b) => a.proposalId - b.proposalId); for (let i = 0; i < genesisStore.proposalSubstore.length; i += 1) { const proposalData = genesisStore.proposalSubstore[i]; if (proposalData.proposalId !== copiedProposalSubstore[i].proposalId) { throw new Error('proposalSubstore must be sorted by proposalId.'); } await proposalStore.set(context, utils_1.bytes.numberToBytes(proposalData.proposalId), proposalData); } const queueStore = this.stores.get(queue_1.ProposalQueueStore); const copiedQueueSubstore = [...genesisStore.queueSubstore]; copiedQueueSubstore.sort((a, b) => a.height - b.height); for (let i = 0; i < genesisStore.queueSubstore.length; i += 1) { const queueData = genesisStore.queueSubstore[i]; if (queueData.height !== copiedQueueSubstore[i].height) { throw new Error('queueSubstore must be sorted by height.'); } await queueStore.set(context, utils_1.bytes.numberToBytes(queueData.height), queueData); } const voteScoreStore = this.stores.get(vote_score_1.VoteScoreStore); const copiedVoteScoreStore = [...genesisStore.voteScoreSubstore]; copiedVoteScoreStore.sort((a, b) => a.address.compare(b.address)); for (let i = 0; i < genesisStore.voteScoreSubstore.length; i += 1) { const voteScoreData = genesisStore.voteScoreSubstore[i]; if (!voteScoreData.address.equals(copiedVoteScoreStore[i].address)) { throw new Error('voteScoreSubstore must be sorted by address.'); } await voteScoreStore.set(context, voteScoreData.address, voteScoreData); } const configRegistryStore = this.stores.get(config_registry_1.ConfigRegistryStore); if (genesisStore.configRegistrySubstore.registry.length > 0) { const copiedConfigRegistrySubstore = [...genesisStore.configRegistrySubstore.registry]; copiedConfigRegistrySubstore.sort((a, b) => { if (a.module > b.module) return -1; if (b.module > a.module) return 1; return 0; }); for (let i = 0; i < genesisStore.configRegistrySubstore.registry.length; i += 1) { const registry = genesisStore.configRegistrySubstore.registry[i]; if (registry.module !== copiedConfigRegistrySubstore[i].module) { throw new Error('configRegistrySubstore.registry must be sorted by module name'); } } await configRegistryStore.set(context, Buffer.alloc(0), genesisStore.configRegistrySubstore); } if (genesisStore.configSubstore.length > 0) { const governableConfigList = this._governableConfig.values(); const copiedConfigSubstore = [...genesisStore.configSubstore]; copiedConfigSubstore.sort((a, b) => { if (a.module > b.module) return -1; if (b.module > a.module) return 1; return 0; }); for (let i = 0; i < genesisStore.configSubstore.length; i += 1) { if (genesisStore.configSubstore[i].module !== copiedConfigSubstore[i].module) { throw new Error('configSubstore must be sorted by module name'); } const governableConfigIndex = governableConfigList.findIndex(t => t.module === genesisStore.configSubstore[i].module); if (governableConfigIndex === -1) { throw new Error(`configSubstore item with module ${genesisStore.configSubstore[i].module} doesnt exists on governableConfigRegistry`); } await governableConfigList[governableConfigIndex].set(context, Buffer.alloc(0), { data: genesisStore.configSubstore[i].data }); } } else { await this._initGovernableConfig(context); } } async _initGovernableConfig(context) { const configRegistryStore = this.stores.get(config_registry_1.ConfigRegistryStore); const configRegisteredEvent = this.events.get(config_registered_1.ConfigRegisteredEvent); const governableConfigList = this._governableConfig.values(); for (const governableConfig of governableConfigList) { await governableConfig.initRegisteredConfig(context); await configRegistryStore.register(context, governableConfig.module, governableConfig.index); configRegisteredEvent.add(context, { module: governableConfig.module, index: governableConfig.index }, [governableConfig.storePrefix]); } } } exports.GovernanceModule = GovernanceModule; //# sourceMappingURL=module.js.map