@swaptoshi/governance-module
Version:
Klayr governance on-chain module
354 lines • 22.6 kB
JavaScript
"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