@swaptoshi/governance-module
Version:
Klayr governance on-chain module
170 lines • 9.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DelegatedVote = void 0;
const cryptography = require("@klayr/cryptography");
const utils = require("@klayr/utils");
const base_1 = require("./base");
const utils_1 = require("@swaptoshi/utils");
const delegated_vote_1 = require("../delegated_vote");
const vote_delegated_1 = require("../../events/vote_delegated");
const delegated_vote_revoked_1 = require("../../events/delegated_vote_revoked");
const vote_score_1 = require("../vote_score");
const casted_vote_1 = require("../casted_vote");
const boosted_account_1 = require("../boosted_account");
class DelegatedVote extends base_1.BaseInstance {
constructor(stores, events, config, genesisConfig, moduleName, delegatedVote, address) {
super(delegated_vote_1.DelegatedVoteStore, stores, events, config, genesisConfig, moduleName, address);
this.outgoingDelegation = Buffer.alloc(0);
this.incomingDelegation = [];
Object.assign(this, utils.objects.cloneDeep(delegatedVote));
this.castedVoteStore = stores.get(casted_vote_1.CastedVoteStore);
this.boostedAccountStore = stores.get(boosted_account_1.BoostedAccountStore);
this.voteScoreStore = stores.get(vote_score_1.VoteScoreStore);
}
toJSON() {
return utils.objects.cloneDeep(utils_1.object.serializer({
outgoingDelegation: this.outgoingDelegation,
incomingDelegation: this.incomingDelegation,
}));
}
toObject() {
return utils.objects.cloneDeep({
outgoingDelegation: this.outgoingDelegation,
incomingDelegation: this.incomingDelegation,
});
}
async verifyDelegateVote(params) {
this._checkImmutableDependencies();
utils_1.verify.verifyAddress('params.delegateeAddress', params.delegateeAddress);
if (Buffer.compare(this.outgoingDelegation, Buffer.alloc(0)) !== 0) {
throw new Error(`sender already delegate their vote, revoke it first!`);
}
const delegateeAccount = await this.instanceStore.getOrDefault(this.immutableContext.context, params.delegateeAddress);
const index = delegateeAccount.incomingDelegation.findIndex(buf => buf.equals(this.immutableContext.senderAddress));
if (index !== -1) {
throw new Error(`sender already exists on ${cryptography.address.getKlayr32AddressFromAddress(params.delegateeAddress)} incoming delegation`);
}
const circularDelegationPath = await this._isCircularDelegation(params.delegateeAddress);
if (circularDelegationPath.length > 0) {
throw new Error(`circular delegation detected: ${circularDelegationPath.map(addr => cryptography.address.getKlayr32AddressFromAddress(addr)).join(' => ')}`);
}
}
async delegateVote(params, verify = true) {
this._checkMutableDependencies();
if (verify)
await this.verifyDelegateVote(params);
await this._removeSenderVoteAndDelegatedVoteFromProposal();
this.outgoingDelegation = params.delegateeAddress;
await this._saveStore();
const delegateeAccount = await this.instanceStore.getOrDefault(this.mutableContext.context, params.delegateeAddress);
delegateeAccount.incomingDelegation.push(this.mutableContext.senderAddress);
await this.instanceStore.set(this.mutableContext.context, params.delegateeAddress, delegateeAccount);
await this._addDelegatedSenderVoteToProposal();
const events = this.events.get(vote_delegated_1.VoteDelegatedEvent);
events.add(this.mutableContext.context, {
delegatorAddress: this.mutableContext.senderAddress,
delegateeAddress: params.delegateeAddress,
}, [this.mutableContext.senderAddress, params.delegateeAddress]);
}
async verifyRevokeDelegatedVote(_params) {
this._checkImmutableDependencies();
if (Buffer.compare(this.outgoingDelegation, Buffer.alloc(0)) === 0) {
throw new Error(`sender is not delegating their vote!`);
}
}
async revokeDelegatedVote(_params, verify = true) {
this._checkMutableDependencies();
if (verify)
await this.verifyRevokeDelegatedVote(_params);
await this._removeSenderVoteAndDelegatedVoteFromProposal();
const delegateeAccount = await this.instanceStore.getOrDefault(this.mutableContext.context, this.outgoingDelegation);
const indexToRemove = delegateeAccount.incomingDelegation.findIndex(buf => buf.equals(this.mutableContext.senderAddress));
if (indexToRemove !== -1)
delegateeAccount.incomingDelegation.splice(indexToRemove, 1);
await this.instanceStore.set(this.mutableContext.context, this.outgoingDelegation, delegateeAccount);
const events = this.events.get(delegated_vote_revoked_1.DelegatedVoteRevokedEvent);
events.add(this.mutableContext.context, {
delegatorAddress: this.mutableContext.senderAddress,
delegateeAddress: this.outgoingDelegation,
}, [this.mutableContext.senderAddress, this.outgoingDelegation]);
this.outgoingDelegation = Buffer.alloc(0);
await this._saveStore();
}
async getIncomingDelegationVoteScore() {
return this._getIncomingDelegationVoteScore(this.key);
}
async _getIncomingDelegationVoteScore(address) {
this._checkImmutableDependencies();
const voteScoreArray = [];
const delegatedVote = await this.instanceStore.getOrDefault(this.immutableContext.context, address);
for (const incomingDelegation of delegatedVote.incomingDelegation) {
const incomingDelegationState = await this.instanceStore.getOrDefault(this.immutableContext.context, incomingDelegation);
if (incomingDelegationState.incomingDelegation.length > 0) {
voteScoreArray.push(...(await this._getIncomingDelegationVoteScore(incomingDelegation)));
}
else {
const voteScore = await this.voteScoreStore.getVoteScore(this.immutableContext.context, incomingDelegation);
const boostingHeight = await this.boostedAccountStore.getOrDefault(this.immutableContext.context, incomingDelegation);
voteScoreArray.push({ voteScore, boostingHeight: boostingHeight.targetHeight });
}
}
return voteScoreArray;
}
async _isCircularDelegation(delegateeAddress) {
this._checkImmutableDependencies();
const visited = new Set();
const path = [];
visited.add(delegateeAddress.toString('hex'));
path.push(delegateeAddress);
return this._checkForCycles(this.immutableContext.senderAddress, visited, path);
}
async _checkForCycles(address, visited, path) {
const addressHex = address.toString('hex');
if (visited.has(addressHex)) {
path.push(address);
const cycleStartIndex = path.findIndex(addr => addr.equals(address));
if (cycleStartIndex !== -1) {
return path.slice(cycleStartIndex);
}
return [];
}
visited.add(addressHex);
path.push(address);
const delegatedVote = await this.instanceStore.getOrDefault(this.immutableContext.context, address);
if (delegatedVote.incomingDelegation.length === 0) {
path.pop();
visited.delete(addressHex);
return [];
}
for (const incomingDelegation of delegatedVote.incomingDelegation) {
const circularRoute = await this._checkForCycles(incomingDelegation, visited, path);
if (circularRoute.length > 0) {
return circularRoute;
}
}
path.pop();
visited.delete(addressHex);
return [];
}
async _removeSenderVoteAndDelegatedVoteFromProposal() {
this._checkMutableDependencies();
if (!this.internalMethod)
throw new Error(`delegatedVote instance is created without internalMethod dependencies`);
const voteScore = await this.voteScoreStore.getVoteScore(this.mutableContext.context, this.mutableContext.senderAddress);
await this.internalMethod.updateProposalVoteSummaryByVoter(this.mutableContext.context, this.mutableContext.senderAddress, BigInt(0), voteScore);
const incomingDelegationVoteScore = await this._getIncomingDelegationVoteScore(this.mutableContext.senderAddress);
await this.internalMethod.updateProposalVoteSummaryByVoter(this.mutableContext.context, this.mutableContext.senderAddress, BigInt(0), incomingDelegationVoteScore);
await this.castedVoteStore.removeAllCastedVote(this.mutableContext.context, this.mutableContext.senderAddress);
}
async _addDelegatedSenderVoteToProposal() {
this._checkMutableDependencies();
if (!this.internalMethod)
throw new Error(`delegatedVote instance is created without internalMethod dependencies`);
const voteScore = await this.voteScoreStore.getVoteScore(this.mutableContext.context, this.mutableContext.senderAddress);
await this.internalMethod.updateProposalVoteSummaryByVoter(this.mutableContext.context, this.mutableContext.senderAddress, voteScore, BigInt(0));
const incomingDelegationVoteScore = await this._getIncomingDelegationVoteScore(this.mutableContext.senderAddress);
await this.internalMethod.updateProposalVoteSummaryByVoter(this.mutableContext.context, this.mutableContext.senderAddress, incomingDelegationVoteScore, BigInt(0));
}
}
exports.DelegatedVote = DelegatedVote;
//# sourceMappingURL=delegated_vote.js.map