Skip to content

Commit

Permalink
Merge pull request #169 from lidofinance/develop
Browse files Browse the repository at this point in the history
Develop to main
  • Loading branch information
Tarens2 authored Dec 15, 2023
2 parents 5f8d4f9 + b9104ee commit a555b67
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 42 deletions.
29 changes: 0 additions & 29 deletions .github/workflows/ci-dev-goerli.yml

This file was deleted.

11 changes: 10 additions & 1 deletion .github/workflows/ci-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,20 @@ jobs:
# needs: test
name: Build and deploy
steps:
- name: Testnet deploy
- name: Holesky testnet deploy
uses: lidofinance/dispatch-workflow@v1
env:
APP_ID: ${{ secrets.APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
TARGET_REPO: "lidofinance/infra-mainnet"
TARGET_WORKFLOW: "deploy_holesky_testnet_withdrawals_api.yaml"
TARGET: "develop"

- name: Goerli testnet deploy
uses: lidofinance/dispatch-workflow@v1
env:
APP_ID: ${{ secrets.APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
TARGET_REPO: "lidofinance/infra-mainnet"
TARGET_WORKFLOW: "deploy_testnet_withdrawals_api.yaml"
TARGET: "develop"
2 changes: 2 additions & 0 deletions src/common/contracts/contracts.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
OracleReportSanityCheckerModule,
AccountingOracleHashConsensusModule,
ValidatorsExitBusOracleHashConsensusModule,
LidoLocatorContractModule,
} from '@lido-nestjs/contracts';
import { Global, Module } from '@nestjs/common';
import { ExecutionProvider } from 'common/execution-provider';
Expand All @@ -16,6 +17,7 @@ import { ExecutionProvider } from 'common/execution-provider';
OracleReportSanityCheckerModule,
AccountingOracleHashConsensusModule,
ValidatorsExitBusOracleHashConsensusModule,
LidoLocatorContractModule,
].map((module) =>
module.forRootAsync({
async useFactory(provider: ExecutionProvider) {
Expand Down
2 changes: 2 additions & 0 deletions src/events/rewards/rewards.constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { CHAINS } from '@lido-nestjs/constants';

export const LIDO_ETH_DESTRIBUTED_EVENT =
'event ETHDistributed(uint256 indexed reportTimestamp, uint256 preCLBalance, uint256 postCLBalance, uint256 withdrawalsWithdrawn, uint256 executionLayerRewardsWithdrawn, uint256 postBufferedEther)';
export const LIDO_EL_REWARDS_RECEIVED_EVENT = 'event ELRewardsReceived(uint256 amount)';
Expand Down
59 changes: 52 additions & 7 deletions src/events/rewards/rewards.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Inject, Injectable } from '@nestjs/common';
import { SECONDS_PER_SLOT, SLOTS_PER_EPOCH } from '../../common/genesis-time';
import { SimpleFallbackJsonRpcBatchProvider } from '@lido-nestjs/execution';
import { Lido, LIDO_CONTRACT_TOKEN } from '@lido-nestjs/contracts';
import {
Lido,
LIDO_CONTRACT_TOKEN,
EXECUTION_REWARDS_VAULT_CONTRACT_ADDRESSES,
LIDO_LOCATOR_CONTRACT_TOKEN,
LidoLocator,
} from '@lido-nestjs/contracts';
import { Interface } from 'ethers';
import {
LIDO_EL_REWARDS_RECEIVED_EVENT,
Expand All @@ -19,6 +25,7 @@ export class RewardsService {
constructor(
@Inject(LOGGER_PROVIDER) protected readonly logger: LoggerService,
@Inject(LIDO_CONTRACT_TOKEN) protected readonly contractLido: Lido,
@Inject(LIDO_LOCATOR_CONTRACT_TOKEN) protected readonly lidoLocator: LidoLocator,
protected readonly rewardsStorage: RewardsStorageService,
protected readonly contractConfig: ContractConfigStorageService,
protected readonly configService: ConfigService,
Expand All @@ -43,12 +50,17 @@ export class RewardsService {

protected async updateRewards(): Promise<void> {
const rewardsPerFrame = await this.getLastTotalRewardsPerFrame();
if (rewardsPerFrame) {
this.rewardsStorage.setRewardsPerFrame(rewardsPerFrame);
}

this.rewardsStorage.setRewardsPerFrame(rewardsPerFrame.allRewards);
this.rewardsStorage.setClRewardsPerFrame(rewardsPerFrame.clRewards);
this.rewardsStorage.setElRewardsPerFrame(rewardsPerFrame.elRewards);
}

public async getLastTotalRewardsPerFrame(): Promise<BigNumber | null> {
public async getLastTotalRewardsPerFrame(): Promise<{
clRewards: BigNumber;
elRewards: BigNumber;
allRewards: BigNumber;
} | null> {
const framesFromLastReport = await this.getFramesFromLastReport();
if (framesFromLastReport === null) {
return null;
Expand All @@ -57,7 +69,11 @@ export class RewardsService {
const { blockNumber, frames } = framesFromLastReport;

if (frames.eq(0)) {
return BigNumber.from(0);
return {
clRewards: BigNumber.from(0),
elRewards: BigNumber.from(0),
allRewards: BigNumber.from(0),
};
}

const { preCLBalance, postCLBalance } = await this.getEthDistributed(blockNumber);
Expand All @@ -67,7 +83,11 @@ export class RewardsService {
const clValidatorsBalanceDiff = postCLBalance.sub(preCLBalance);
const clRewards = clValidatorsBalanceDiff.add(withdrawalsReceived);

return clRewards.add(elRewards).div(frames);
return {
clRewards: clRewards.div(frames),
elRewards: elRewards.div(frames),
allRewards: clRewards.add(elRewards).div(frames),
};
}

protected async get48HoursAgoBlock() {
Expand Down Expand Up @@ -165,4 +185,29 @@ export class RewardsService {
),
};
}

async getVaultsBalance() {
const chainId = this.configService.get('CHAIN_ID');
const withdrawalVaultAddress = await this.lidoLocator.withdrawalVault();
const withdrawalVaultBalance = await this.provider.getBalance(withdrawalVaultAddress);
const rewardsVaultBalance = await this.provider.getBalance(EXECUTION_REWARDS_VAULT_CONTRACT_ADDRESSES[chainId]);
const elRewards = this.rewardsStorage.getElRewardsPerFrame();
const clRewards = this.rewardsStorage.getClRewardsPerFrame();

// note: it is pessimistic, we can sub rewards partially depending on amount of time past
const diffCl = withdrawalVaultBalance.sub(clRewards);
const diffEl = rewardsVaultBalance.sub(elRewards);

let balance = BigNumber.from(0);

if (diffEl.gt(0)) {
balance = balance.add(diffEl);
}

if (diffCl.gt(0)) {
balance = balance.add(diffCl);
}

return balance;
}
}
2 changes: 2 additions & 0 deletions src/http/request-time/request-time.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ConfigModule } from 'common/config';
import { GenesisTimeModule } from 'common/genesis-time';
import { RequestTimeController } from './request-time.controller';
import { RequestTimeService } from './request-time.service';
import { RewardsModule } from '../../events/rewards';

@Module({
imports: [
Expand All @@ -18,6 +19,7 @@ import { RequestTimeService } from './request-time.service';
ConfigModule,
GenesisTimeModule,
RewardsStorageModule,
RewardsModule,
],
controllers: [RequestTimeController],
providers: [RequestTimeService],
Expand Down
28 changes: 23 additions & 5 deletions src/http/request-time/request-time.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { RequestTimeCalculationType } from './dto/request-time-calculation-type'
import { RequestsTimeOptionsDto } from './dto/requests-time-options.dto';
import { FAR_FUTURE_EPOCH } from '../../jobs/validators/validators.constants';
import { Lido, LIDO_CONTRACT_TOKEN, WITHDRAWAL_QUEUE_CONTRACT_TOKEN, WithdrawalQueue } from '@lido-nestjs/contracts';
import { RewardsService } from '../../events/rewards';

@Injectable()
export class RequestTimeService {
Expand All @@ -42,6 +43,7 @@ export class RequestTimeService {
protected readonly configService: ConfigService,
protected readonly genesisTimeService: GenesisTimeService,
protected readonly rewardsStorage: RewardsStorageService,
protected readonly rewardsService: RewardsService,
protected readonly contractConfig: ContractConfigStorageService,
) {}

Expand Down Expand Up @@ -75,11 +77,13 @@ export class RequestTimeService {
amount: string,
unfinalized?: BigNumber,
depositable?: BigNumber,
vaultsBalance?: BigNumber,
): Promise<RequestTimeV2Dto | null> {
const nextCalculationAt = this.queueInfo.getNextUpdate().toISOString();
const validatorsLastUpdate = this.validators.getLastUpdate();
const unfinalizedETH = unfinalized ?? (await this.contractWithdrawal.unfinalizedStETH()); // do runtime request if empty param
const depositableETH = depositable ?? (await this.contractLido.getDepositableEther()); // do runtime request if empty param
const vaultsBalanceETH = vaultsBalance ?? (await this.rewardsService.getVaultsBalance());

if (!unfinalizedETH || !validatorsLastUpdate) {
return {
Expand All @@ -94,7 +98,12 @@ export class RequestTimeService {

const latestEpoch = this.validators.getMaxExitEpoch();

const { ms, type } = await this.calculateWithdrawalTimeV2(queueStETH, depositableETH, Date.now(), latestEpoch);
const { ms, type } = await this.calculateWithdrawalTimeV2(
queueStETH,
depositableETH.add(vaultsBalanceETH),
Date.now(),
latestEpoch,
);

return {
requestInfo: {
Expand All @@ -112,6 +121,7 @@ export class RequestTimeService {
unfinalized: BigNumber,
buffer: BigNumber,
depositable: BigNumber,
vaultsBalance: BigNumber,
): Promise<RequestTimeByRequestIdDto | null> {
const requests = this.queueInfo.getRequests();
const validatorsLastUpdate = this.validators.getLastUpdate();
Expand Down Expand Up @@ -153,14 +163,19 @@ export class RequestTimeService {

if (!request && BigNumber.from(requestId).gte(lastRequestId)) {
// for not found requests return calculating status with 0 eth
const lastRequestResult: RequestTimeByRequestIdDto = await this.getRequestTimeV2('0', unfinalized, depositable);
const lastRequestResult: RequestTimeByRequestIdDto = await this.getRequestTimeV2(
'0',
unfinalized,
depositable,
vaultsBalance,
);
lastRequestResult.status = RequestTimeStatus.calculating;
lastRequestResult.requestInfo.requestId = requestId;
return lastRequestResult;
}

const queueStETH = this.calculateUnfinalizedEthForRequestId(requests, request);
const depositableForRequest = buffer.sub(queueStETH).add(request.amountOfStETH);
const depositableForRequest = buffer.add(vaultsBalance).sub(queueStETH).add(request.amountOfStETH);
const requestTimestamp = request.timestamp.toNumber() * 1000;
const currentExitValidatorsDiffEpochs = Number(maxExitEpoch) - currentEpoch;
const maxExitEpochInPast =
Expand Down Expand Up @@ -344,13 +359,16 @@ export class RequestTimeService {
}

async getTimeRequests(requestOptions: RequestsTimeOptionsDto) {
const [unfinalized, buffer, depositable] = await Promise.all([
const [unfinalized, buffer, depositable, vaultsBalance] = await Promise.all([
this.contractWithdrawal.unfinalizedStETH(),
this.contractLido.getBufferedEther(),
this.contractLido.getDepositableEther(),
this.rewardsService.getVaultsBalance(),
]);

return Promise.all(requestOptions.ids.map((id) => this.getTimeByRequestId(id, unfinalized, buffer, depositable)));
return Promise.all(
requestOptions.ids.map((id) => this.getTimeByRequestId(id, unfinalized, buffer, depositable, vaultsBalance)),
);
}

public validateRequestTimeOptions(params: RequestTimeOptionsDto) {
Expand Down
18 changes: 18 additions & 0 deletions src/storage/rewards/rewards.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { BigNumber } from '@ethersproject/bignumber';
@Injectable()
export class RewardsStorageService {
protected rewardsPerFrame: BigNumber;
protected clRewardsPerFrame: BigNumber;
protected elRewardsPerFrame: BigNumber;

public getRewardsPerFrame() {
return this.rewardsPerFrame;
Expand All @@ -12,4 +14,20 @@ export class RewardsStorageService {
public setRewardsPerFrame(rewardsPerFrame: BigNumber) {
this.rewardsPerFrame = rewardsPerFrame;
}

public getElRewardsPerFrame() {
return this.elRewardsPerFrame;
}

public setElRewardsPerFrame(elRewardsPerFrame: BigNumber) {
this.elRewardsPerFrame = elRewardsPerFrame;
}

public getClRewardsPerFrame() {
return this.elRewardsPerFrame;
}

public setClRewardsPerFrame(clRewardsPerFrame: BigNumber) {
this.clRewardsPerFrame = clRewardsPerFrame;
}
}

0 comments on commit a555b67

Please sign in to comment.