UNPKG

@celo/contractkit

Version:

Celo's ContractKit to interact with Celo network

317 lines 16.4 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.LockedGoldWrapper = void 0; const collections_1 = require("@celo/base/lib/collections"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const BaseWrapper_1 = require("../wrappers/BaseWrapper"); const BaseWrapperForGoverning_1 = require("./BaseWrapperForGoverning"); const bigNumberComparator = (a, b) => a.lt(b); function linkedListChanges(groups, changed) { return (0, collections_1.linkedListChanges)(groups, changed, bigNumberComparator); } /** * Contract for handling deposits needed for voting. */ class LockedGoldWrapper extends BaseWrapperForGoverning_1.BaseWrapperForGoverning { constructor() { super(...arguments); /** * Withdraws a gold that has been unlocked after the unlocking period has passed. * @param index The index of the pending withdrawal to withdraw. */ this.withdraw = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.withdraw); /** * Locks gold to be used for voting. * The gold to be locked, must be specified as the `tx.value` */ this.lock = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.lock); /** * Delegates locked gold. */ this.delegate = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.delegateGovernanceVotes); /** * Updates the amount of delegated locked gold. There might be discrepancy between the amount of locked gold * and the amount of delegated locked gold because of received rewards. */ this.updateDelegatedAmount = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.updateDelegatedAmount); /** * Revokes delegated locked gold. */ this.revokeDelegated = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.revokeDelegatedGovernanceVotes); this.getMaxDelegateesCount = () => __awaiter(this, void 0, void 0, function* () { const maxDelegateesCountHex = yield this.connection.web3.eth.getStorageAt( // @ts-ignore this.contract._address, 10); return new bignumber_js_1.default(maxDelegateesCountHex, 16); }); this.getDelegateInfo = (account) => __awaiter(this, void 0, void 0, function* () { const totalDelegatedFractionPromise = this.contract.methods .getAccountTotalDelegatedFraction(account) .call(); const totalDelegatedCeloPromise = this.contract.methods.totalDelegatedCelo(account).call(); const delegateesPromise = this.contract.methods.getDelegateesOfDelegator(account).call(); const fixidity = new bignumber_js_1.default('1000000000000000000000000'); return { totalPercentDelegated: new bignumber_js_1.default(yield totalDelegatedFractionPromise) .multipliedBy(100) .div(fixidity) .toFixed() + '%', delegatees: yield delegateesPromise, totalVotesDelegatedToThisAccount: new bignumber_js_1.default(yield totalDelegatedCeloPromise), }; }); /** * Unlocks gold that becomes withdrawable after the unlocking period. * @param value The amount of gold to unlock. */ this.unlock = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.unlock, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.valueToString)); /** * Relocks gold that has been unlocked but not withdrawn. * @param index The index of the pending withdrawal to relock from. * @param value The value to relock from the specified pending withdrawal. */ this._relock = (0, BaseWrapper_1.proxySend)(this.connection, this.contract.methods.relock, (0, BaseWrapper_1.tupleParser)(BaseWrapper_1.valueToString, BaseWrapper_1.valueToString)); /** * Returns the total amount of locked gold for an account. * @param account The account. * @return The total amount of locked gold for an account. */ this.getAccountTotalLockedGold = (0, BaseWrapper_1.proxyCall)(this.contract.methods.getAccountTotalLockedGold, undefined, BaseWrapper_1.valueToBigNumber); /** * Returns the total amount of locked gold in the system. Note that this does not include * gold that has been unlocked but not yet withdrawn. * @returns The total amount of locked gold in the system. */ this.getTotalLockedGold = (0, BaseWrapper_1.proxyCall)(this.contract.methods.getTotalLockedGold, undefined, BaseWrapper_1.valueToBigNumber); /** * Returns the total amount of non-voting locked gold for an account. * @param account The account. * @return The total amount of non-voting locked gold for an account. */ this.getAccountNonvotingLockedGold = (0, BaseWrapper_1.proxyCall)(this.contract.methods.getAccountNonvotingLockedGold, undefined, BaseWrapper_1.valueToBigNumber); this._getTotalPendingWithdrawalsCount = (0, BaseWrapper_1.proxyCall)(this.contract.methods.getTotalPendingWithdrawalsCount, undefined, BaseWrapper_1.valueToBigNumber); } getPendingWithdrawalsTotalValue(account) { return __awaiter(this, void 0, void 0, function* () { const pendingWithdrawals = yield this.getPendingWithdrawals(account); // Ensure there are enough pending withdrawals to relock. const values = pendingWithdrawals.map((pw) => pw.value); const reducer = (total, pw) => pw.plus(total); return values.reduce(reducer, new bignumber_js_1.default(0)); }); } /** * Relocks gold that has been unlocked but not withdrawn. * @param value The value to relock from pending withdrawals. */ relock(account, value) { return __awaiter(this, void 0, void 0, function* () { const pendingWithdrawals = yield this.getPendingWithdrawals(account); // Ensure there are enough pending withdrawals to relock. const totalValue = yield this.getPendingWithdrawalsTotalValue(account); if (totalValue.isLessThan(value)) { throw new Error(`Not enough pending withdrawals to relock ${value}`); } // Assert pending withdrawals are sorted by time (increasing), so that we can re-lock starting // with those furthest away from being available (at the end). const throwIfNotSorted = (pw, i) => { if (i > 0 && !pw.time.isGreaterThanOrEqualTo(pendingWithdrawals[i - 1].time)) { throw new Error('Pending withdrawals not sorted by timestamp'); } }; pendingWithdrawals.forEach(throwIfNotSorted); let remainingToRelock = new bignumber_js_1.default(value); const relockPw = (acc, pw, i) => { const valueToRelock = bignumber_js_1.default.minimum(pw.value, remainingToRelock); if (!valueToRelock.isZero()) { remainingToRelock = remainingToRelock.minus(valueToRelock); acc.push(this._relock(i, valueToRelock)); } return acc; }; return pendingWithdrawals.reduceRight(relockPw, []); }); } /** * Returns current configuration parameters. */ getConfig() { return __awaiter(this, void 0, void 0, function* () { return { unlockingPeriod: (0, BaseWrapper_1.valueToBigNumber)(yield this.contract.methods.unlockingPeriod().call()), totalLockedGold: yield this.getTotalLockedGold(), }; }); } /** * @dev Returns human readable configuration of the lockedcelo contract * @return LockedGoldConfig object */ getHumanReadableConfig() { return __awaiter(this, void 0, void 0, function* () { const config = yield this.getConfig(); return Object.assign(Object.assign({}, config), { unlockingPeriod: (0, BaseWrapper_1.secondsToDurationString)(config.unlockingPeriod) }); }); } getAccountSummary(account) { return __awaiter(this, void 0, void 0, function* () { const validators = yield this.contracts.getValidators(); const nonvotingPromise = this.getAccountNonvotingLockedGold(account); const totalPromise = this.getAccountTotalLockedGold(account); const requirementPromise = validators.getAccountLockedGoldRequirement(account); const pendingWithdrawalsPromise = this.getPendingWithdrawals(account); const accountTotalGovernanceVotingPowerPromise = this.getAccountTotalGovernanceVotingPower(account); return { lockedGold: { total: yield totalPromise, nonvoting: yield nonvotingPromise, requirement: yield requirementPromise, }, totalGovernaneVotingPower: yield accountTotalGovernanceVotingPowerPromise, pendingWithdrawals: yield pendingWithdrawalsPromise, }; }); } /** * Returns the total amount of governance voting power for an account. * @param account The address of the account. * @return The total amount of governance voting power for an account. */ getAccountTotalGovernanceVotingPower(account) { return __awaiter(this, void 0, void 0, function* () { const totalGovernanceVotingPower = yield this.contract.methods .getAccountTotalGovernanceVotingPower(account) .call(); return new bignumber_js_1.default(totalGovernanceVotingPower); }); } /** * Returns the pending withdrawals from unlocked gold for an account. * @param account The address of the account. * @return The value and timestamp for each pending withdrawal. */ getPendingWithdrawals(account) { return __awaiter(this, void 0, void 0, function* () { const withdrawals = yield this.contract.methods.getPendingWithdrawals(account).call(); return (0, collections_1.zip)((time, value) => ({ time: (0, BaseWrapper_1.valueToBigNumber)(time), value: (0, BaseWrapper_1.valueToBigNumber)(value), }), withdrawals[1], withdrawals[0]); }); } /** * Returns the pending withdrawal at a given index for a given account. * @param account The address of the account. * @param index The index of the pending withdrawal. * @return The value of the pending withdrawal. * @return The timestamp of the pending withdrawal. */ getPendingWithdrawal(account, index) { return __awaiter(this, void 0, void 0, function* () { const response = yield this.contract.methods.getPendingWithdrawal(account, index).call(); return { value: (0, BaseWrapper_1.valueToBigNumber)(response[0]), time: (0, BaseWrapper_1.valueToBigNumber)(response[1]), }; }); } /** * Retrieves AccountSlashed for epochNumber. * @param epochNumber The epoch to retrieve AccountSlashed at. */ getAccountsSlashed(epochNumber) { return __awaiter(this, void 0, void 0, function* () { const epochManagerWrapper = yield this.contracts.getEpochManager(); const [fromBlock, toBlock] = yield Promise.all([ epochManagerWrapper.getFirstBlockAtEpoch(epochNumber), epochManagerWrapper.getLastBlockAtEpoch(epochNumber), ]); const events = yield this.getPastEvents('AccountSlashed', { fromBlock, toBlock }); return events.map((e) => ({ epochNumber, slashed: e.returnValues.slashed, penalty: (0, BaseWrapper_1.valueToBigNumber)(e.returnValues.penalty), reporter: e.returnValues.reporter, reward: (0, BaseWrapper_1.valueToBigNumber)(e.returnValues.reward), })); }); } /** * Computes parameters for slashing `penalty` from `account`. * @param account The account to slash. * @param penalty The amount to slash as penalty. * @return List of (group, voting gold) to decrement from `account`. */ computeInitialParametersForSlashing(account, penalty) { return __awaiter(this, void 0, void 0, function* () { const election = yield this.contracts.getElection(); const eligible = yield election.getEligibleValidatorGroupsVotes(); const groups = eligible.map((x) => ({ address: x.address, value: x.votes })); return this.computeParametersForSlashing(account, penalty, groups); }); } computeParametersForSlashing(account, penalty, groups) { return __awaiter(this, void 0, void 0, function* () { const changed = yield this.computeDecrementsForSlashing(account, penalty, groups); const changes = linkedListChanges(groups, changed); return Object.assign(Object.assign({}, changes), { indices: changed.map((a) => a.index) }); }); } // Returns how much voting gold will be decremented from the groups voted by an account // Implementation follows protocol/test/common/integration slashingOfGroups() computeDecrementsForSlashing(account, penalty, allGroups) { var _a; return __awaiter(this, void 0, void 0, function* () { // first check how much voting gold has to be slashed const nonVoting = yield this.getAccountNonvotingLockedGold(account); if (penalty.isLessThan(nonVoting)) { return []; } let difference = penalty.minus(nonVoting); // find voted groups const election = yield this.contracts.getElection(); const groups = yield election.getGroupsVotedForByAccount(account); const res = []; // for (let i = groups.length - 1; i >= 0; i--) { const group = groups[i]; const totalVotes = (_a = allGroups.find((a) => a.address === group)) === null || _a === void 0 ? void 0 : _a.value; if (!totalVotes) { throw new Error(`Cannot find group ${group}`); } const votes = yield election.getTotalVotesForGroupByAccount(group, account); const slashedVotes = votes.lt(difference) ? votes : difference; res.push({ address: group, value: totalVotes.minus(slashedVotes), index: i }); difference = difference.minus(slashedVotes); if (difference.eq(new bignumber_js_1.default(0))) { break; } } return res; }); } /** * Returns the number of pending withdrawals for the specified account. * @param account The account. * @returns The count of pending withdrawals. */ getTotalPendingWithdrawalsCount(account) { return __awaiter(this, void 0, void 0, function* () { return this._getTotalPendingWithdrawalsCount(account); }); } } exports.LockedGoldWrapper = LockedGoldWrapper; //# sourceMappingURL=LockedGold.js.map