@celo/contractkit
Version:
Celo's ContractKit to interact with Celo network
727 lines • 35.8 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GovernanceWrapper = exports.hotfixToParams = exports.VoteValue = exports.proposalToParams = exports.ProposalStage = void 0;
const address_1 = require("@celo/base/lib/address");
const async_1 = require("@celo/base/lib/async");
const collections_1 = require("@celo/base/lib/collections");
const connect_1 = require("@celo/connect");
const fixidity_1 = require("@celo/utils/lib/fixidity");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const BaseWrapper_1 = require("./BaseWrapper");
const BaseWrapperForGoverning_1 = require("./BaseWrapperForGoverning");
var ProposalStage;
(function (ProposalStage) {
ProposalStage["None"] = "None";
ProposalStage["Queued"] = "Queued";
ProposalStage["Approval"] = "Approval";
ProposalStage["Referendum"] = "Referendum";
ProposalStage["Execution"] = "Execution";
ProposalStage["Expiration"] = "Expiration";
})(ProposalStage || (exports.ProposalStage = ProposalStage = {}));
const proposalToParams = (proposal, descriptionURL) => {
const data = proposal.map((tx) => (0, address_1.hexToBuffer)(tx.input));
return [
proposal.map((tx) => tx.value),
proposal.map((tx) => tx.to),
(0, BaseWrapper_1.bufferToSolidityBytes)(Buffer.concat(data)),
data.map((inp) => inp.length),
descriptionURL,
];
};
exports.proposalToParams = proposalToParams;
var VoteValue;
(function (VoteValue) {
VoteValue["None"] = "None";
VoteValue["Abstain"] = "Abstain";
VoteValue["No"] = "No";
VoteValue["Yes"] = "Yes";
})(VoteValue || (exports.VoteValue = VoteValue = {}));
const hotfixToParams = (proposal, salt) => {
const p = (0, exports.proposalToParams)(proposal, ''); // no description URL for hotfixes
return [p[0], p[1], p[2], p[3], (0, address_1.bufferToHex)(salt)];
};
exports.hotfixToParams = hotfixToParams;
const ZERO_BN = new bignumber_js_1.default(0);
/**
* Contract managing voting for governance proposals.
*/
class GovernanceWrapper extends BaseWrapperForGoverning_1.BaseWrapperForGoverning {
constructor() {
super(...arguments);
/**
* Querying number of possible concurrent proposals.
* @returns Current number of possible concurrent proposals.
*/
this.concurrentProposals = (0, BaseWrapper_1.proxyCall)(this.contract.methods.concurrentProposals, undefined, BaseWrapper_1.valueToBigNumber);
/**
* Query time of last proposal dequeue
* @returns Time of last dequeue
*/
this.lastDequeue = (0, BaseWrapper_1.proxyCall)(this.contract.methods.lastDequeue, undefined, BaseWrapper_1.valueToBigNumber);
/**
* Query proposal dequeue frequency.
* @returns Current proposal dequeue frequency in seconds.
*/
this.dequeueFrequency = (0, BaseWrapper_1.proxyCall)(this.contract.methods.dequeueFrequency, undefined, BaseWrapper_1.valueToBigNumber);
/**
* Query minimum deposit required to make a proposal.
* @returns Current minimum deposit.
*/
this.minDeposit = (0, BaseWrapper_1.proxyCall)(this.contract.methods.minDeposit, undefined, BaseWrapper_1.valueToBigNumber);
/**
* Query queue expiry parameter.
* @return The number of seconds a proposal can stay in the queue before expiring.
*/
this.queueExpiry = (0, BaseWrapper_1.proxyCall)(this.contract.methods.queueExpiry, undefined, BaseWrapper_1.valueToBigNumber);
/**
* Returns whether or not a particular account is voting on proposals.
* @param account The address of the account.
* @returns Whether or not the account is voting on proposals.
*/
this.isVoting = (0, BaseWrapper_1.proxyCall)(this.contract.methods.isVoting);
/**
* Returns the metadata associated with a given proposal.
* @param proposalID Governance proposal UUID
*/
this.getProposalMetadata = (0, BaseWrapper_1.proxyCall)(this.contract.methods.getProposal, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.valueToString), (res) => ({
proposer: res[0],
deposit: (0, BaseWrapper_1.valueToBigNumber)(res[1]),
timestamp: (0, BaseWrapper_1.valueToBigNumber)(res[2]),
transactionCount: (0, BaseWrapper_1.valueToInt)(res[3]),
descriptionURL: res[4],
}));
/**
* Returns the transaction at the given index associated with a given proposal.
* @param proposalID Governance proposal UUID
* @param txIndex Transaction index
*/
this.getProposalTransaction = (0, BaseWrapper_1.proxyCall)(this.contract.methods.getProposalTransaction, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.valueToString, BaseWrapper_1.valueToString), (res) => ({
value: res[0],
to: res[1],
input: (0, BaseWrapper_1.solidityBytesToString)(res[2]),
}));
/**
* Returns whether a given proposal is approved.
* @param proposalID Governance proposal UUID
*/
this.isApproved = (0, BaseWrapper_1.proxyCall)(this.contract.methods.isApproved, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.valueToString));
/**
* Returns whether a dequeued proposal is expired.
* @param proposalID Governance proposal UUID
*/
this.isDequeuedProposalExpired = (0, BaseWrapper_1.proxyCall)(this.contract.methods.isDequeuedProposalExpired, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.valueToString));
/**
* Returns whether a dequeued proposal is expired.
* @param proposalID Governance proposal UUID
*/
this.isQueuedProposalExpired = (0, BaseWrapper_1.proxyCall)(this.contract.methods.isQueuedProposalExpired, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.valueToString));
/**
* Returns the approver address for proposals and hotfixes.
*/
this.getApprover = (0, BaseWrapper_1.proxyCall)(this.contract.methods.approver);
/**
* Returns the approver multisig contract for proposals and hotfixes.
*/
this.getApproverMultisig = () => this.getApprover().then((address) => this.contracts.getMultiSig(address));
/**
* Returns the security council address for hotfixes.
*/
this.getSecurityCouncil = (0, BaseWrapper_1.proxyCall)(this.contract.methods.securityCouncil);
/**
* Returns the security council multisig contract for hotfixes.
*/
this.getSecurityCouncilMultisig = () => this.getSecurityCouncil().then((address) => this.contracts.getMultiSig(address));
this.getProposalStage = (proposalID) => __awaiter(this, void 0, void 0, function* () {
const queue = yield this.getQueue();
const existsInQueue = queue.find((u) => u.proposalID === proposalID) !== undefined;
if (existsInQueue) {
const expired = yield this.isQueuedProposalExpired(proposalID);
return expired ? ProposalStage.Expiration : ProposalStage.Queued;
}
const res = yield this.contract.methods.getProposalStage((0, BaseWrapper_1.valueToString)(proposalID)).call();
return Object.keys(ProposalStage)[(0, BaseWrapper_1.valueToInt)(res)];
});
/**
* Returns whether a given proposal is passing relative to the constitution's threshold.
* @param proposalID Governance proposal UUID
*/
this.isProposalPassing = (0, BaseWrapper_1.proxyCall)(this.contract.methods.isProposalPassing, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.valueToString));
/**
* Withdraws refunded proposal deposits.
*/
this.withdraw = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.withdraw);
/**
* Submits a new governance proposal.
* @param proposal Governance proposal
* @param descriptionURL A URL where further information about the proposal can be viewed
*/
this.propose = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.propose, exports.proposalToParams);
/**
* Returns whether a governance proposal exists with the given ID.
* @param proposalID Governance proposal UUID
*/
this.proposalExists = (0, BaseWrapper_1.proxyCall)(this.contract.methods.proposalExists, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.valueToString));
/**
* Returns the current upvoted governance proposal ID and applied vote weight (zeroes if none).
* @param upvoter Address of upvoter
*/
this.getUpvoteRecord = (0, BaseWrapper_1.proxyCall)(this.contract.methods.getUpvoteRecord, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.identity), (o) => ({
proposalID: (0, BaseWrapper_1.valueToBigNumber)(o[0]),
upvotes: (0, BaseWrapper_1.valueToBigNumber)(o[1]),
}));
/**
* Returns whether a given proposal is queued.
* @param proposalID Governance proposal UUID
*/
this.isQueued = (0, BaseWrapper_1.proxyCall)(this.contract.methods.isQueued, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.valueToString));
/**
* Returns the value of proposal deposits that have been refunded.
* @param proposer Governance proposer address.
*/
this.getRefundedDeposits = (0, BaseWrapper_1.proxyCall)(this.contract.methods.refundedDeposits, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.stringIdentity), BaseWrapper_1.valueToBigNumber);
/*
* Returns the upvotes applied to a given proposal.
* @param proposalID Governance proposal UUID
*/
this.getUpvotes = (0, BaseWrapper_1.proxyCall)(this.contract.methods.getUpvotes, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.valueToString), BaseWrapper_1.valueToBigNumber);
/**
* Returns the yes, no, and abstain votes applied to a given proposal.
* @param proposalID Governance proposal UUID
*/
this.getVotes = (0, BaseWrapper_1.proxyCall)(this.contract.methods.getVoteTotals, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.valueToString), (res) => ({
[VoteValue.Yes]: (0, BaseWrapper_1.valueToBigNumber)(res[0]),
[VoteValue.No]: (0, BaseWrapper_1.valueToBigNumber)(res[1]),
[VoteValue.Abstain]: (0, BaseWrapper_1.valueToBigNumber)(res[2]),
}));
/**
* Returns the proposal queue as list of upvote records.
*/
this.getQueue = (0, BaseWrapper_1.proxyCall)(this.contract.methods.getQueue, undefined, (arraysObject) => (0, collections_1.zip)((_id, _upvotes) => ({
proposalID: (0, BaseWrapper_1.valueToBigNumber)(_id),
upvotes: (0, BaseWrapper_1.valueToBigNumber)(_upvotes),
}), arraysObject[0], arraysObject[1]));
/**
* Dequeues any queued proposals if `dequeueFrequency` seconds have elapsed since the last dequeue
*/
this.dequeueProposalsIfReady = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.dequeueProposalsIfReady);
this.revokeVotes = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.revokeVotes);
this.getHotfixHash = (0, BaseWrapper_1.proxyCall)(this.contract.methods.getHotfixHash, exports.hotfixToParams);
/**
* Returns the number of validators required to reach a Byzantine quorum
*/
this.minQuorumSize = (0, BaseWrapper_1.proxyCall)(this.contract.methods.minQuorumSizeInCurrentSet, undefined, BaseWrapper_1.valueToBigNumber);
/**
* Marks the given hotfix approved by `sender`.
* @param hash keccak256 hash of hotfix's associated abi encoded transactions
* @notice Only the `approver` address will succeed in sending this transaction
*/
this.approveHotfix = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.approveHotfix, (0, BaseWrapper_1.tupleParser)(address_1.bufferToHex));
/**
* Marks the given hotfix prepared for current epoch if quorum of validators have whitelisted it.
* @param hash keccak256 hash of hotfix's associated abi encoded transactions
*/
this.prepareHotfix = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.prepareHotfix, (0, BaseWrapper_1.tupleParser)(address_1.bufferToHex));
/**
* Executes a given sequence of transactions if the corresponding hash is prepared and approved.
* @param hotfix Governance hotfix proposal
* @param salt Secret which guarantees uniqueness of hash
* @notice keccak256 hash of abi encoded transactions computed on-chain
*/
this.executeHotfix = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.executeHotfix, exports.hotfixToParams);
}
/**
* Query durations of different stages in proposal lifecycle.
* @returns Durations for approval, referendum and execution stages in seconds.
*/
stageDurations() {
return __awaiter(this, void 0, void 0, function* () {
const res = yield this.contract.methods.stageDurations().call();
return {
[ProposalStage.Referendum]: (0, BaseWrapper_1.valueToBigNumber)(res[1]),
[ProposalStage.Execution]: (0, BaseWrapper_1.valueToBigNumber)(res[2]),
};
});
}
/**
* Returns the required ratio of yes:no votes needed to exceed in order to pass the proposal transaction.
* @param tx Transaction to determine the constitution for running.
*/
getTransactionConstitution(tx) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// Extract the leading four bytes of the call data, which specifies the function.
const callSignature = (0, address_1.ensureLeading0x)((0, address_1.trimLeading0x)(tx.input).slice(0, 8));
const value = yield this.contract.methods
.getConstitution((_a = tx.to) !== null && _a !== void 0 ? _a : address_1.NULL_ADDRESS, callSignature)
.call();
return (0, fixidity_1.fromFixed)(new bignumber_js_1.default(value));
});
}
/**
* Returns the required ratio of yes:no votes needed to exceed in order to pass the proposal.
* @param proposal Proposal to determine the constitution for running.
*/
getConstitution(proposal) {
return __awaiter(this, void 0, void 0, function* () {
// Default value that is harcoded on Governance contract
// it's 0.5 in Fixidity
// https://github.com/celo-org/celo-monorepo/blob/3fffa158d67ffd6366e81ba7243eadede1974b1b/packages/protocol/contracts/governance/Governance.sol#L39
let constitution = (0, fixidity_1.fromFixed)(new bignumber_js_1.default('500000000000000000000000'));
for (const tx of proposal) {
constitution = bignumber_js_1.default.max(yield this.getTransactionConstitution(tx), constitution);
}
return constitution;
});
}
/**
* Returns the participation parameters.
* @returns The participation parameters.
*/
getParticipationParameters() {
return __awaiter(this, void 0, void 0, function* () {
const res = yield this.contract.methods.getParticipationParameters().call();
return {
baseline: (0, fixidity_1.fromFixed)(new bignumber_js_1.default(res[0])),
baselineFloor: (0, fixidity_1.fromFixed)(new bignumber_js_1.default(res[1])),
baselineUpdateFactor: (0, fixidity_1.fromFixed)(new bignumber_js_1.default(res[2])),
baselineQuorumFactor: (0, fixidity_1.fromFixed)(new bignumber_js_1.default(res[3])),
};
});
}
// function get support doesn't consider constitution parameteres that has an influence
// in the total of yes votes required
getSupportWithConstitutionThreshold(proposalID, constitution) {
return __awaiter(this, void 0, void 0, function* () {
const support = yield this.getSupport(proposalID);
support.required = support.required.times(constitution).integerValue();
return support;
});
}
// simulates proposal.getSupportWithQuorumPadding
getSupport(proposalID) {
return __awaiter(this, void 0, void 0, function* () {
const [participation, votes, lockedGold] = yield Promise.all([
this.getParticipationParameters(),
this.getVotes(proposalID),
this.contracts.getLockedGold(),
]);
const quorum = participation.baseline.times(participation.baselineQuorumFactor);
const total = votes.Yes.plus(votes.No).plus(votes.Abstain);
// NOTE: this networkWeight is not as governance calculates it,
// but we don't have access to proposal.networkWeight
const networkWeight = yield lockedGold.getTotalLockedGold();
const required = networkWeight.times(quorum);
let support = votes.Yes.div(votes.Yes.plus(votes.No));
support = isNaN(support.toNumber()) ? new bignumber_js_1.default(0) : support;
return {
support,
required,
total,
};
});
}
/**
* Returns current configuration parameters.
*/
getConfig() {
return __awaiter(this, void 0, void 0, function* () {
const res = yield Promise.all([
this.concurrentProposals(),
this.dequeueFrequency(),
this.minDeposit(),
this.queueExpiry(),
this.stageDurations(),
this.getParticipationParameters(),
]);
return {
concurrentProposals: res[0],
dequeueFrequency: res[1],
minDeposit: res[2],
queueExpiry: res[3],
stageDurations: res[4],
participationParameters: res[5],
};
});
}
/**
* @dev Returns human readable configuration of the governance contract
* @return GovernanceConfig object
*/
getHumanReadableConfig() {
return __awaiter(this, void 0, void 0, function* () {
const config = yield this.getConfig();
const stageDurations = {
[ProposalStage.Referendum]: (0, BaseWrapper_1.secondsToDurationString)(config.stageDurations[ProposalStage.Referendum]),
[ProposalStage.Execution]: (0, BaseWrapper_1.secondsToDurationString)(config.stageDurations[ProposalStage.Execution]),
};
return Object.assign(Object.assign({}, config), { dequeueFrequency: (0, BaseWrapper_1.secondsToDurationString)(config.dequeueFrequency), queueExpiry: (0, BaseWrapper_1.secondsToDurationString)(config.queueExpiry), stageDurations });
});
}
/**
* Returns the human readable metadata associated with a given proposal.
* @param proposalID Governance proposal UUID
*/
getHumanReadableProposalMetadata(proposalID) {
return __awaiter(this, void 0, void 0, function* () {
const meta = yield this.getProposalMetadata(proposalID);
return Object.assign(Object.assign({}, meta), { timestamp: (0, BaseWrapper_1.unixSecondsTimestampToDateString)(meta.timestamp) });
});
}
proposalSchedule(proposalID) {
return __awaiter(this, void 0, void 0, function* () {
const meta = yield this.getProposalMetadata(proposalID);
const stage = yield this.getProposalStage(proposalID);
if (stage === ProposalStage.Queued) {
const queueExpiry = yield this.queueExpiry();
const queueExpiration = meta.timestamp.plus(queueExpiry);
return {
[ProposalStage.Queued]: meta.timestamp,
[ProposalStage.Expiration]: queueExpiration,
};
}
const durations = yield this.stageDurations();
const referendum = meta.timestamp;
const execution = referendum.plus(durations.Referendum);
const expiration = execution.plus(durations.Execution);
return {
[ProposalStage.Referendum]: referendum,
[ProposalStage.Execution]: execution,
[ProposalStage.Expiration]: expiration,
};
});
}
humanReadableProposalSchedule(proposalID) {
return __awaiter(this, void 0, void 0, function* () {
const schedule = yield this.proposalSchedule(proposalID);
const dates = {};
for (const stage of Object.keys(schedule)) {
dates[stage] = (0, BaseWrapper_1.unixSecondsTimestampToDateString)(schedule[stage]);
}
return dates;
});
}
/**
* Returns the proposal associated with a given id.
* @param proposalID Governance proposal UUID
*/
getProposal(proposalID) {
return __awaiter(this, void 0, void 0, function* () {
const metadata = yield this.getProposalMetadata(proposalID);
const txIndices = (0, collections_1.zeroRange)(metadata.transactionCount);
return (0, async_1.concurrentMap)(4, txIndices, (idx) => this.getProposalTransaction(proposalID, idx));
});
}
getApprovalStatus(proposalID) {
return __awaiter(this, void 0, void 0, function* () {
const [multisig, approveTx] = yield Promise.all([
this.getApproverMultisig(),
this.approve(proposalID),
]);
const [multisigTxs, approvers] = yield Promise.all([
multisig.getTransactionDataByContent(this.address, approveTx.txo),
multisig.getOwners(),
]);
const confirmations = multisigTxs ? multisigTxs.confirmations : [];
return {
completion: `${confirmations.length} / ${approvers.length}`,
confirmations,
approvers,
};
});
}
/**
* Returns the stage, metadata, upvotes, votes, and transactions associated with a given proposal.
* @param proposalID Governance proposal UUID
*/
getProposalRecord(proposalID) {
return __awaiter(this, void 0, void 0, function* () {
const [proposal, metadata, stage] = yield Promise.all([
this.getProposal(proposalID),
this.getProposalMetadata(proposalID),
this.getProposalStage(proposalID),
]);
const record = {
proposal,
metadata,
stage,
passed: false,
approved: false,
};
if (stage === ProposalStage.Queued) {
record.upvotes = yield this.getUpvotes(proposalID);
}
else if (stage === ProposalStage.Referendum || stage === ProposalStage.Execution) {
const [passed, votes, approved, approvals] = yield Promise.all([
this.isProposalPassing(proposalID),
this.getVotes(proposalID),
this.isApproved(proposalID),
this.getApprovalStatus(proposalID),
]);
record.passed = passed;
record.votes = votes;
record.approved = approved;
record.approvals = approvals;
}
return record;
});
}
isUpvoting(upvoter) {
return __awaiter(this, void 0, void 0, function* () {
const upvote = yield this.getUpvoteRecord(upvoter);
return (!upvote.proposalID.isZero() &&
(yield this.isQueued(upvote.proposalID)) &&
!(yield this.isQueuedProposalExpired(upvote.proposalID)));
});
}
/**
* Returns the corresponding vote record
* @param voter Address of voter
* @param proposalID Governance proposal UUID
*/
getVoteRecord(voter, proposalID) {
return __awaiter(this, void 0, void 0, function* () {
try {
const proposalIndex = yield this.getDequeueIndex(proposalID);
const res = yield this.contract.methods.getVoteRecord(voter, proposalIndex).call();
return {
proposalID: (0, BaseWrapper_1.valueToBigNumber)(res[0]),
value: Object.keys(VoteValue)[(0, BaseWrapper_1.valueToInt)(res[1])],
votes: (0, BaseWrapper_1.valueToBigNumber)(res[2]),
yesVotes: (0, BaseWrapper_1.valueToBigNumber)(res[3]),
noVotes: (0, BaseWrapper_1.valueToBigNumber)(res[4]),
abstainVotes: (0, BaseWrapper_1.valueToBigNumber)(res[5]),
};
}
catch (_) {
// The proposal ID may not be present in the dequeued list, or the voter may not have a vote
// record for the proposal.
return null;
}
});
}
/**
* Returns the (existing) proposal dequeue as list of proposal IDs.
*/
getDequeue(filterZeroes = false) {
return __awaiter(this, void 0, void 0, function* () {
const dequeue = yield this.contract.methods.getDequeue().call();
// filter non-zero as dequeued indices are reused and `deleteDequeuedProposal` zeroes
const dequeueIds = dequeue.map(BaseWrapper_1.valueToBigNumber);
return filterZeroes ? dequeueIds.filter((id) => !id.isZero()) : dequeueIds;
});
}
/*
* Returns the vote records for a given voter.
*/
getVoteRecords(voter) {
return __awaiter(this, void 0, void 0, function* () {
const dequeue = yield this.getDequeue();
const voteRecords = yield Promise.all(dequeue.map((id) => this.getVoteRecord(voter, id)));
return voteRecords.filter((record) => record != null);
});
}
isVotingReferendum(voter) {
return __awaiter(this, void 0, void 0, function* () {
const records = yield this.getVoteRecords(voter);
return records.length !== 0;
});
}
/*
* Returns information pertaining to a voter in governance.
*/
getVoter(account) {
return __awaiter(this, void 0, void 0, function* () {
const res = yield Promise.all([
this.getUpvoteRecord(account),
this.getVoteRecords(account),
this.getRefundedDeposits(account),
]);
return {
upvote: res[0],
votes: res[1],
refundedDeposits: res[2],
};
});
}
/**
* Returns the number of votes that will be applied to a proposal for a given voter.
* @param voter Address of voter
*/
getVoteWeight(voter) {
return __awaiter(this, void 0, void 0, function* () {
const lockedGoldContract = yield this.contracts.getLockedGold();
return lockedGoldContract.getAccountTotalLockedGold(voter);
});
}
getIndex(id, array) {
const index = array.findIndex((bn) => bn.isEqualTo(id));
if (index === -1) {
throw new Error(`ID ${id} not found in array ${array}`);
}
return index;
}
getDequeueIndex(proposalID, dequeue) {
return __awaiter(this, void 0, void 0, function* () {
if (!dequeue) {
dequeue = yield this.getDequeue();
}
return this.getIndex(proposalID, dequeue);
});
}
getQueueIndex(proposalID, queue) {
return __awaiter(this, void 0, void 0, function* () {
if (!queue) {
queue = yield this.getQueue();
}
return {
index: this.getIndex(proposalID, queue.map((record) => record.proposalID)),
queue,
};
});
}
lesserAndGreater(proposalID, _queue) {
return __awaiter(this, void 0, void 0, function* () {
const { index, queue } = yield this.getQueueIndex(proposalID, _queue);
return {
lesserID: index === 0 ? ZERO_BN : queue[index - 1].proposalID,
greaterID: index === queue.length - 1 ? ZERO_BN : queue[index + 1].proposalID,
};
});
}
sortedQueue(queue) {
return queue.sort((a, b) => a.upvotes.comparedTo(b.upvotes));
}
withUpvoteRevoked(upvoter, _queue) {
return __awaiter(this, void 0, void 0, function* () {
const upvoteRecord = yield this.getUpvoteRecord(upvoter);
const { index, queue } = yield this.getQueueIndex(upvoteRecord.proposalID, _queue);
queue[index].upvotes = queue[index].upvotes.minus(upvoteRecord.upvotes);
return {
queue: this.sortedQueue(queue),
upvoteRecord,
};
});
}
withUpvoteApplied(upvoter, proposalID, _queue) {
return __awaiter(this, void 0, void 0, function* () {
const { index, queue } = yield this.getQueueIndex(proposalID, _queue);
const weight = yield this.getVoteWeight(upvoter);
queue[index].upvotes = queue[index].upvotes.plus(weight);
return this.sortedQueue(queue);
});
}
lesserAndGreaterAfterRevoke(upvoter) {
return __awaiter(this, void 0, void 0, function* () {
const { queue, upvoteRecord } = yield this.withUpvoteRevoked(upvoter);
return this.lesserAndGreater(upvoteRecord.proposalID, queue);
});
}
lesserAndGreaterAfterUpvote(upvoter, proposalID) {
return __awaiter(this, void 0, void 0, function* () {
const upvoteRecord = yield this.getUpvoteRecord(upvoter);
const recordQueued = yield this.isQueued(upvoteRecord.proposalID);
// if existing upvote exists in queue, revoke it before applying new upvote
const queue = recordQueued
? (yield this.withUpvoteRevoked(upvoter)).queue
: yield this.getQueue();
const upvoteQueue = yield this.withUpvoteApplied(upvoter, proposalID, queue);
return this.lesserAndGreater(proposalID, upvoteQueue);
});
}
/**
* Applies provided upvoter's upvote to given proposal.
* @param proposalID Governance proposal UUID
* @param upvoter Address of upvoter
*/
upvote(proposalID, upvoter) {
return __awaiter(this, void 0, void 0, function* () {
const { lesserID, greaterID } = yield this.lesserAndGreaterAfterUpvote(upvoter, proposalID);
return (0, connect_1.toTransactionObject)(this.connection, this.contract.methods.upvote((0, BaseWrapper_1.valueToString)(proposalID), (0, BaseWrapper_1.valueToString)(lesserID), (0, BaseWrapper_1.valueToString)(greaterID)));
});
}
/**
* Revokes provided upvoter's upvote.
* @param upvoter Address of upvoter
*/
revokeUpvote(upvoter) {
return __awaiter(this, void 0, void 0, function* () {
const { lesserID, greaterID } = yield this.lesserAndGreaterAfterRevoke(upvoter);
return (0, connect_1.toTransactionObject)(this.connection, this.contract.methods.revokeUpvote((0, BaseWrapper_1.valueToString)(lesserID), (0, BaseWrapper_1.valueToString)(greaterID)));
});
}
/**
* Approves given proposal, allowing it to later move to `referendum`.
* @param proposalID Governance proposal UUID
* @notice Only the `approver` address will succeed in sending this transaction
*/
approve(proposalID) {
return __awaiter(this, void 0, void 0, function* () {
const proposalIndex = yield this.getDequeueIndex(proposalID);
return (0, connect_1.toTransactionObject)(this.connection, this.contract.methods.approve((0, BaseWrapper_1.valueToString)(proposalID), proposalIndex));
});
}
/**
* Applies `sender`'s vote choice to a given proposal.
* @param proposalID Governance proposal UUID
* @param vote Choice to apply (yes, no, abstain)
*/
vote(proposalID, vote) {
return __awaiter(this, void 0, void 0, function* () {
const proposalIndex = yield this.getDequeueIndex(proposalID);
const voteNum = Object.keys(VoteValue).indexOf(vote);
return (0, connect_1.toTransactionObject)(this.connection, this.contract.methods.vote((0, BaseWrapper_1.valueToString)(proposalID), proposalIndex, voteNum));
});
}
/**
* Applies `sender`'s vote choice to a given proposal.
* @param proposalID Governance proposal UUID.
* @param yesVotes The yes votes.
* @param noVotes The no votes.
* @param abstainVotes The abstain votes.
*/
votePartially(proposalID, yesVotes, noVotes, abstainVotes) {
return __awaiter(this, void 0, void 0, function* () {
const proposalIndex = yield this.getDequeueIndex(proposalID);
return (0, connect_1.toTransactionObject)(this.connection, this.contract.methods.votePartially((0, BaseWrapper_1.valueToString)(proposalID), proposalIndex, (0, BaseWrapper_1.valueToString)(yesVotes), (0, BaseWrapper_1.valueToString)(noVotes), (0, BaseWrapper_1.valueToString)(abstainVotes)));
});
}
/**
* Executes a given proposal's associated transactions.
* @param proposalID Governance proposal UUID
*/
execute(proposalID) {
return __awaiter(this, void 0, void 0, function* () {
const proposalIndex = yield this.getDequeueIndex(proposalID);
return (0, connect_1.toTransactionObject)(this.connection, this.contract.methods.execute((0, BaseWrapper_1.valueToString)(proposalID), proposalIndex));
});
}
/**
* Returns approved, executed, and prepared status associated with a given hotfix.
* @param hash keccak256 hash of hotfix's associated abi encoded transactions
*/
getHotfixRecord(hash) {
return __awaiter(this, void 0, void 0, function* () {
const res = yield this.contract.methods.getHotfixRecord((0, address_1.bufferToHex)(hash)).call();
return {
approved: res[0],
councilApproved: res[1],
executed: res[2],
executionTimeLimit: (0, BaseWrapper_1.valueToBigNumber)(res[3]),
};
});
}
}
exports.GovernanceWrapper = GovernanceWrapper;
//# sourceMappingURL=Governance.js.map