UNPKG

@celo/contractkit

Version:

Celo's ContractKit to interact with Celo network

727 lines 35.8 kB
"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