UNPKG

@lidofinance/lido-ethereum-sdk

Version:

<div style="display: flex;" align="center"> <h1 align="center">Lido Ethereum SDK</h1> </div>

382 lines 22.9 kB
"use strict"; var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { var useValue = arguments.length > 2; for (var i = 0; i < initializers.length; i++) { value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); } return useValue ? value : void 0; }; var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); var _, done = false; for (var i = decorators.length - 1; i >= 0; i--) { var context = {}; for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; for (var p in contextIn.access) context.access[p] = contextIn.access[p]; context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); if (kind === "accessor") { if (result === void 0) continue; if (result === null || typeof result !== "object") throw new TypeError("Object expected"); if (_ = accept(result.get)) descriptor.get = _; if (_ = accept(result.set)) descriptor.set = _; if (_ = accept(result.init)) initializers.unshift(_); } else if (_ = accept(result)) { if (kind === "field") initializers.unshift(_); else descriptor[key] = _; } } if (target) Object.defineProperty(target, contextIn.name, descriptor); done = true; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LidoSDKRewards = void 0; const viem_1 = require("viem"); const index_js_1 = require("../common/decorators/index.js"); const sdk_module_js_1 = require("../common/class-primitives/sdk-module.js"); const rewardsEvents_js_1 = require("./abi/rewardsEvents.js"); const constants_js_1 = require("../common/constants.js"); const index_js_2 = require("./subgraph/index.js"); const index_js_3 = require("./subgraph/index.js"); const utils_js_1 = require("./utils.js"); const apr_js_1 = require("../statistics/apr.js"); const index_js_4 = require("../common/index.js"); let LidoSDKRewards = (() => { var _a; let _classSuper = sdk_module_js_1.LidoSDKModule; let _instanceExtraInitializers = []; let _contractAddressStETH_decorators; let _contractAddressWithdrawalQueue_decorators; let _earliestRebaseEventBlock_decorators; let _getContractStETH_decorators; let _getRewardsFromChain_decorators; let _getRewardsFromSubgraph_decorators; return _a = class LidoSDKRewards extends _classSuper { async contractAddressStETH() { return await this.core.getContractAddress(constants_js_1.LIDO_CONTRACT_NAMES.lido); } async contractAddressWithdrawalQueue() { return await this.core.getContractAddress(constants_js_1.LIDO_CONTRACT_NAMES.withdrawalQueue); } earliestRebaseEventBlock() { const block = constants_js_1.EARLIEST_TOKEN_REBASED_EVENT[this.core.chainId]; (0, index_js_4.invariant)(block, `No rebase event for chain:${this.core.chainId}`, index_js_4.ERROR_CODE.NOT_SUPPORTED); return block; } async getContractStETH() { const address = await this.contractAddressStETH(); return (0, viem_1.getContract)({ address, abi: rewardsEvents_js_1.rewardsEventsAbi, client: this.core.publicClient, }); } async getRewardsFromChain(props) { const [{ address, fromBlock, toBlock, includeZeroRebases, includeOnlyRebases }, stethContract, withdrawalQueueAddress,] = await Promise.all([ this.parseProps(props), this.getContractStETH(), this.contractAddressWithdrawalQueue(), ]); const step = props.stepBlock ?? _a.DEFAULT_STEP_BLOCK; (0, index_js_4.invariantArgument)(step > 0, 'stepBlock must be a positive integer'); const lowerBound = this.earliestRebaseEventBlock(); if (fromBlock < lowerBound) throw this.core.error({ message: `Cannot index events earlier than first TokenRebased event at block ${lowerBound.toString()}`, code: index_js_4.ERROR_CODE.NOT_SUPPORTED, }); const preBlock = fromBlock === 0n ? 0n : fromBlock - 1n; const [baseBalanceShares, baseTotalEther, baseTotalShares, transferOutEvents, transferInEvents, rebaseEvents,] = await (0, index_js_4.withSDKError)(Promise.all([ stethContract.read.sharesOf([address], { blockNumber: preBlock, }), stethContract.read.getTotalPooledEther({ blockNumber: preBlock }), stethContract.read.getTotalShares({ blockNumber: preBlock }), (0, utils_js_1.requestWithBlockStep)(step, fromBlock, toBlock, (fromBlock, toBlock) => stethContract.getEvents.TransferShares({ from: address }, { fromBlock, toBlock })), (0, utils_js_1.requestWithBlockStep)(step, fromBlock, toBlock, (fromBlock, toBlock) => stethContract.getEvents.TransferShares({ to: address }, { fromBlock, toBlock })), (0, utils_js_1.requestWithBlockStep)(step, fromBlock, toBlock, (fromBlock, toBlock) => stethContract.getEvents.TokenRebased({}, { fromBlock, toBlock, })), ]), index_js_4.ERROR_CODE.READ_ERROR); const events = [].concat(transferInEvents, transferOutEvents, rebaseEvents); events.sort((event1, event2) => { const block = event1.blockNumber - event2.blockNumber; if (block === 0n) { return event1.logIndex - event2.logIndex; } return block > 0n ? 1 : -1; }); let currentTotalEther = baseTotalEther; let currentTotalShares = baseTotalShares; const getCurrentStethFromShares = (shares) => (0, utils_js_1.sharesToSteth)(shares, currentTotalEther, currentTotalShares, _a.PRECISION); const getCurrentShareRate = () => (0, utils_js_1.calcShareRate)(currentTotalEther, currentTotalShares, _a.PRECISION); const baseBalance = getCurrentStethFromShares(baseBalanceShares); const baseShareRate = getCurrentShareRate(); let totalRewards = 0n; let shareRate = baseShareRate; let prevSharesBalance = baseBalanceShares; let rewards = events .map((event) => { if (event.eventName === 'TransferShares') { const { from, to, sharesValue } = event.args; if ((0, viem_1.isAddressEqual)(to, from)) { return null; } let type, changeShares, balanceShares; if ((0, viem_1.isAddressEqual)(to, address)) { type = (0, viem_1.isAddressEqual)(from, viem_1.zeroAddress) ? 'submit' : 'transfer_in'; balanceShares = prevSharesBalance + sharesValue; changeShares = sharesValue; } else { type = (0, viem_1.isAddressEqual)(to, withdrawalQueueAddress) ? 'withdrawal' : 'transfer_out'; balanceShares = prevSharesBalance - sharesValue; changeShares = -sharesValue; } prevSharesBalance = balanceShares; return { type, balanceShares, changeShares, change: getCurrentStethFromShares(changeShares), balance: getCurrentStethFromShares(balanceShares), shareRate, originalEvent: event, }; } if (event.eventName === 'TokenRebased') { const { postTotalEther, postTotalShares } = event.args; const oldBalance = getCurrentStethFromShares(prevSharesBalance); currentTotalEther = postTotalEther; currentTotalShares = postTotalShares; const newBalance = getCurrentStethFromShares(prevSharesBalance); shareRate = getCurrentShareRate(); const change = newBalance - oldBalance; totalRewards += change; return { type: 'rebase', change, apr: apr_js_1.LidoSDKApr.calculateAprFromRebaseEvent(event.args), changeShares: 0n, balance: newBalance, balanceShares: prevSharesBalance, shareRate, originalEvent: event, }; } (0, index_js_4.invariant)(false, 'Impossible event'); }) .filter((event) => !!event); if (includeOnlyRebases) { rewards = rewards.filter((r) => r.type === 'rebase'); } if (!includeZeroRebases) { rewards = rewards.filter((r) => !(r.type === 'rebase' && r.change === 0n)); } return { rewards, baseBalanceShares, baseShareRate, baseBalance, totalRewards, fromBlock: fromBlock, toBlock: toBlock, }; } async getRewardsFromSubgraph(props) { const [{ getSubgraphUrl, address, fromBlock, toBlock, includeZeroRebases, includeOnlyRebases, }, withdrawalQueueAddress,] = await Promise.all([ this.parseProps(props), this.contractAddressWithdrawalQueue(), ]); const url = getSubgraphUrl(this.core.getSubgraphId(), this.core.chainId); const step = props.stepEntities ?? _a.DEFAULT_STEP_ENTITIES; (0, index_js_4.invariantArgument)(step > 0, 'stepEntities must be a positive integer'); const lastIndexedBlock = BigInt((await (0, index_js_2.getLastIndexedBlock)({ url })).number); const cappedToBlock = lastIndexedBlock < toBlock ? lastIndexedBlock : toBlock; const preBlock = fromBlock === 0n ? 0n : fromBlock - 1n; const [transfers, rebases, { transfer: initialTransfer, rebase: initialRebase },] = await (0, index_js_4.withSDKError)(Promise.all([ (0, index_js_2.getTransfers)({ url, address, fromBlock, toBlock: cappedToBlock, step, }), (0, index_js_2.getTotalRewards)({ url, fromBlock, toBlock: cappedToBlock, step }), (0, index_js_3.getInitialData)({ url, address, block: preBlock }), ]), index_js_4.ERROR_CODE.READ_ERROR); const events = [].concat(rebases, transfers); events.sort((event1, event2) => { const block = BigInt(event1.block) - BigInt(event2.block); if (block === 0n) { return Number(event1.logIndex) - Number(event2.logIndex); } return block > 0n ? 1 : -1; }); let prevBalanceShares = 0n; let prevBalance = 0n; let baseShareRate = 0; if (initialTransfer) { const { to, from, balanceAfterDecrease, balanceAfterIncrease, sharesAfterDecrease, sharesAfterIncrease, } = initialTransfer; if ((0, viem_1.isAddressEqual)(to, address)) { prevBalanceShares = BigInt(sharesAfterIncrease); prevBalance = BigInt(balanceAfterIncrease); } else if ((0, viem_1.isAddressEqual)(from, address)) { prevBalanceShares = BigInt(sharesAfterDecrease); prevBalance = BigInt(balanceAfterDecrease); } } if (initialRebase) { const { totalPooledEtherAfter, totalSharesAfter } = initialRebase; const totalEther = BigInt(totalPooledEtherAfter); const totalShares = BigInt(totalSharesAfter); baseShareRate = (0, utils_js_1.calcShareRate)(totalEther, totalShares, _a.PRECISION); prevBalance = (0, utils_js_1.sharesToSteth)(prevBalanceShares, totalEther, totalShares, _a.PRECISION); } const baseBalance = prevBalance; const baseBalanceShares = prevBalanceShares; let totalRewards = 0n; let rewards = events .map((event) => { if ('value' in event) { const { from, to, shares, sharesAfterIncrease, value, balanceAfterDecrease, balanceAfterIncrease, sharesAfterDecrease, totalPooledEther, totalShares, } = event; let type, changeShares, balanceShares, change, balance; if ((0, viem_1.isAddressEqual)(to, from)) { return null; } if ((0, viem_1.isAddressEqual)(to, address)) { type = (0, viem_1.isAddressEqual)(from, viem_1.zeroAddress) ? 'submit' : 'transfer_in'; changeShares = BigInt(shares); balanceShares = BigInt(sharesAfterIncrease); change = BigInt(value); balance = BigInt(balanceAfterIncrease); } else { type = (0, viem_1.isAddressEqual)(to, withdrawalQueueAddress) ? 'withdrawal' : 'transfer_out'; balance = BigInt(balanceAfterDecrease); change = -BigInt(value); changeShares = -BigInt(shares); balanceShares = BigInt(sharesAfterDecrease); } const shareRate = (0, utils_js_1.calcShareRate)(BigInt(totalPooledEther), BigInt(totalShares), _a.PRECISION); prevBalance = balance; prevBalanceShares = balanceShares; return { type, balanceShares, changeShares, change, balance, shareRate, originalEvent: event, }; } if ('apr' in event) { const { totalPooledEtherAfter, totalSharesAfter, apr: eventApr, } = event; const totalEther = BigInt(totalPooledEtherAfter); const totalShares = BigInt(totalSharesAfter); const newBalance = (0, utils_js_1.sharesToSteth)(prevBalanceShares, totalEther, totalShares, _a.PRECISION); const change = newBalance - prevBalance; totalRewards += change; prevBalance = newBalance; return { type: 'rebase', change, apr: Number(eventApr), changeShares: 0n, balance: newBalance, balanceShares: prevBalanceShares, shareRate: (0, utils_js_1.calcShareRate)(totalEther, totalShares, _a.PRECISION), originalEvent: event, }; } (0, index_js_4.invariant)(false, 'impossible event'); }) .filter((events) => !!events); if (includeOnlyRebases) { rewards = rewards.filter((r) => r.type === 'rebase'); } if (!includeZeroRebases) { rewards = rewards.filter((r) => !(r.type === 'rebase' && r.change === 0n)); } return { rewards, baseBalance, lastIndexedBlock, baseBalanceShares, totalRewards, baseShareRate, fromBlock, toBlock: cappedToBlock, }; } async parseProps(props) { const toBlock = await this.core.toBlockNumber(props.to ?? { block: 'latest' }); const fromBlock = props.from ? await this.core.toBlockNumber(props.from) : await this.core.toBackBlock(props.back, toBlock); (0, index_js_4.invariantArgument)(toBlock >= fromBlock, 'toBlock is lower than fromBlock'); const { includeZeroRebases = false, includeOnlyRebases = false } = props; return { ...props, fromBlock, includeZeroRebases, includeOnlyRebases, toBlock, }; } constructor() { super(...arguments); __runInitializers(this, _instanceExtraInitializers); } }, (() => { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0; _contractAddressStETH_decorators = [(0, index_js_1.Logger)('Contracts:'), (0, index_js_1.Cache)(30 * 60 * 1000, ['core.chain.id'])]; _contractAddressWithdrawalQueue_decorators = [(0, index_js_1.Logger)('Contracts:'), (0, index_js_1.Cache)(30 * 60 * 1000, ['core.chain.id'])]; _earliestRebaseEventBlock_decorators = [(0, index_js_1.Logger)('Contracts:'), (0, index_js_1.Cache)(30 * 60 * 1000, ['core.chain.id'])]; _getContractStETH_decorators = [(0, index_js_1.Logger)('Contracts:'), (0, index_js_1.Cache)(30 * 60 * 1000, ['core.chain.id', 'contractAddressStETH'])]; _getRewardsFromChain_decorators = [(0, index_js_1.Logger)('Rewards:'), (0, index_js_1.ErrorHandler)('Rewards:')]; _getRewardsFromSubgraph_decorators = [(0, index_js_1.Logger)('Rewards:'), (0, index_js_1.ErrorHandler)('Rewards:')]; __esDecorate(_a, null, _contractAddressStETH_decorators, { kind: "method", name: "contractAddressStETH", static: false, private: false, access: { has: obj => "contractAddressStETH" in obj, get: obj => obj.contractAddressStETH }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _contractAddressWithdrawalQueue_decorators, { kind: "method", name: "contractAddressWithdrawalQueue", static: false, private: false, access: { has: obj => "contractAddressWithdrawalQueue" in obj, get: obj => obj.contractAddressWithdrawalQueue }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _earliestRebaseEventBlock_decorators, { kind: "method", name: "earliestRebaseEventBlock", static: false, private: false, access: { has: obj => "earliestRebaseEventBlock" in obj, get: obj => obj.earliestRebaseEventBlock }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _getContractStETH_decorators, { kind: "method", name: "getContractStETH", static: false, private: false, access: { has: obj => "getContractStETH" in obj, get: obj => obj.getContractStETH }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _getRewardsFromChain_decorators, { kind: "method", name: "getRewardsFromChain", static: false, private: false, access: { has: obj => "getRewardsFromChain" in obj, get: obj => obj.getRewardsFromChain }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _getRewardsFromSubgraph_decorators, { kind: "method", name: "getRewardsFromSubgraph", static: false, private: false, access: { has: obj => "getRewardsFromSubgraph" in obj, get: obj => obj.getRewardsFromSubgraph }, metadata: _metadata }, null, _instanceExtraInitializers); if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); })(), Object.defineProperty(_a, "PRECISION", { enumerable: true, configurable: true, writable: true, value: 10n ** 27n }), Object.defineProperty(_a, "DEFAULT_STEP_ENTITIES", { enumerable: true, configurable: true, writable: true, value: 1000 }), Object.defineProperty(_a, "DEFAULT_STEP_BLOCK", { enumerable: true, configurable: true, writable: true, value: 50000 }), _a; })(); exports.LidoSDKRewards = LidoSDKRewards; //# sourceMappingURL=rewards.js.map