diff --git a/src/common/registry/fetch/interfaces/operator.interface.ts b/src/common/registry/fetch/interfaces/operator.interface.ts index cedeedb4..e4d20826 100644 --- a/src/common/registry/fetch/interfaces/operator.interface.ts +++ b/src/common/registry/fetch/interfaces/operator.interface.ts @@ -7,4 +7,5 @@ export interface RegistryOperator { stoppedValidators: number; totalSigningKeys: number; usedSigningKeys: number; + moduleAddress: string; } diff --git a/src/common/registry/fetch/key-batch.fetch.ts b/src/common/registry/fetch/key-batch.fetch.ts index 2dac85b8..cc0331b0 100644 --- a/src/common/registry/fetch/key-batch.fetch.ts +++ b/src/common/registry/fetch/key-batch.fetch.ts @@ -1,19 +1,22 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Registry, REGISTRY_CONTRACT_TOKEN } from '@lido-nestjs/contracts'; +import { Injectable } from '@nestjs/common'; +import { Registry__factory } from '@lido-nestjs/contracts'; import { CallOverrides } from './interfaces/overrides.interface'; import { KeyBatchRecord, RegistryKey } from './interfaces/key.interface'; import { RegistryOperatorFetchService } from './operator.fetch'; import { KEYS_LENGTH, SIGNATURE_LENGTH } from './key-batch.constants'; +import { ExecutionProvider } from 'common/execution-provider'; @Injectable() export class RegistryKeyBatchFetchService { constructor( - @Inject(REGISTRY_CONTRACT_TOKEN) - private contract: Registry, - - private operatorsService: RegistryOperatorFetchService, + protected readonly provider: ExecutionProvider, + protected readonly operatorsService: RegistryOperatorFetchService, ) {} + private getContract(moduleAddress: string) { + return Registry__factory.connect(moduleAddress, this.provider); + } + /** * Split one big string into array of strings * `0x${key1}{key2}...` -> `[`0x${key1}`, `0x${key2}`]` @@ -116,7 +119,11 @@ export class RegistryKeyBatchFetchService { const currentBatchSize = Math.min(batchSize, totalAmount - i * batchSize); const promise = (async () => { - const keys = await this.contract.getSigningKeys(operatorIndex, currentFromIndex, currentBatchSize); + const keys = await this.getContract(moduleAddress).getSigningKeys( + operatorIndex, + currentFromIndex, + currentBatchSize, + ); return this.formatKeys(operatorIndex, keys, currentFromIndex, moduleAddress); })(); diff --git a/src/common/registry/fetch/key.fetch.ts b/src/common/registry/fetch/key.fetch.ts index 344ab187..36666869 100644 --- a/src/common/registry/fetch/key.fetch.ts +++ b/src/common/registry/fetch/key.fetch.ts @@ -1,20 +1,20 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { rangePromise } from '@lido-nestjs/utils'; -import { Registry, REGISTRY_CONTRACT_TOKEN } from '@lido-nestjs/contracts'; +import { Registry__factory } from '@lido-nestjs/contracts'; import { CallOverrides } from './interfaces/overrides.interface'; import { RegistryKey } from './interfaces/key.interface'; import { RegistryOperatorFetchService } from './operator.fetch'; import { REGISTRY_KEY_BATCH_SIZE } from './key.constants'; +import { ExecutionProvider } from 'common/execution-provider'; @Injectable() export class RegistryKeyFetchService { - constructor( - @Inject(REGISTRY_CONTRACT_TOKEN) - private contract: Registry, + constructor(protected readonly provider: ExecutionProvider, private operatorsService: RegistryOperatorFetchService) {} - private operatorsService: RegistryOperatorFetchService, - ) {} + private getContract(moduleAddress: string) { + return Registry__factory.connect(moduleAddress, this.provider); + } /** fetches one key */ public async fetchOne( @@ -23,7 +23,7 @@ export class RegistryKeyFetchService { moduleAddress: string, overrides: CallOverrides = {}, ): Promise { - const keyData = await this.contract.getSigningKey(operatorIndex, keyIndex, overrides as any); + const keyData = await this.getContract(moduleAddress).getSigningKey(operatorIndex, keyIndex, overrides as any); const { key, depositSignature, used } = keyData; diff --git a/src/common/registry/fetch/meta.fetch.ts b/src/common/registry/fetch/meta.fetch.ts index 365cde89..0621d381 100644 --- a/src/common/registry/fetch/meta.fetch.ts +++ b/src/common/registry/fetch/meta.fetch.ts @@ -1,16 +1,21 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Inject, Injectable } from '@nestjs/common'; -import { Registry, REGISTRY_CONTRACT_TOKEN } from '@lido-nestjs/contracts'; +import { Injectable } from '@nestjs/common'; +import { Registry__factory } from '@lido-nestjs/contracts'; import { CallOverrides } from './interfaces/overrides.interface'; +import { ExecutionProvider } from 'common/execution-provider'; @Injectable() export class RegistryMetaFetchService { - constructor(@Inject(REGISTRY_CONTRACT_TOKEN) private registryContract: Registry) {} + constructor(protected readonly provider: ExecutionProvider) {} + + private getContract(moduleAddress: string) { + return Registry__factory.connect(moduleAddress, this.provider); + } /** fetches keys operation index */ - public async fetchKeysOpIndex(overrides: CallOverrides = {}, moduleAddress: string): Promise { + public async fetchKeysOpIndex(moduleAddress: string, overrides: CallOverrides = {}): Promise { // TODO: read data from all contract that implement curated-v1-onchain type - const bigNumber = await this.registryContract.getKeysOpIndex(overrides as any); + const bigNumber = await this.getContract(moduleAddress).getKeysOpIndex(overrides as any); return bigNumber.toNumber(); } } diff --git a/src/common/registry/fetch/operator.fetch.ts b/src/common/registry/fetch/operator.fetch.ts index 8e4ef502..b21af545 100644 --- a/src/common/registry/fetch/operator.fetch.ts +++ b/src/common/registry/fetch/operator.fetch.ts @@ -1,21 +1,23 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { rangePromise } from '@lido-nestjs/utils'; -import { Registry, REGISTRY_CONTRACT_TOKEN } from '@lido-nestjs/contracts'; +import { Registry__factory } from '@lido-nestjs/contracts'; import { CallOverrides } from './interfaces/overrides.interface'; import { RegistryOperator } from './interfaces/operator.interface'; import { REGISTRY_OPERATORS_BATCH_SIZE } from './operator.constants'; +import { ExecutionProvider } from 'common/execution-provider'; @Injectable() export class RegistryOperatorFetchService { - constructor( - @Inject(REGISTRY_CONTRACT_TOKEN) - private contract: Registry, - ) {} + constructor(protected readonly provider: ExecutionProvider) {} + + private getContract(moduleAddress: string) { + return Registry__factory.connect(moduleAddress, this.provider); + } /** fetches number of operators */ - public async count(overrides: CallOverrides = {}): Promise { - const bigNumber = await this.contract.getNodeOperatorsCount(overrides as any); + public async count(moduleAddress: string, overrides: CallOverrides = {}): Promise { + const bigNumber = await this.getContract(moduleAddress).getNodeOperatorsCount(overrides as any); return bigNumber.toNumber(); } @@ -26,7 +28,7 @@ export class RegistryOperatorFetchService { overrides: CallOverrides = {}, ): Promise { const fullInfo = true; - const operator = await this.contract.getNodeOperator(operatorIndex, fullInfo, overrides as any); + const operator = await this.getContract(moduleAddress).getNodeOperator(operatorIndex, fullInfo, overrides as any); const { name, @@ -47,6 +49,7 @@ export class RegistryOperatorFetchService { stoppedValidators: totalExitedValidators.toNumber(), totalSigningKeys: totalAddedValidators.toNumber(), usedSigningKeys: totalDepositedValidators.toNumber(), + moduleAddress, }; } @@ -62,7 +65,7 @@ export class RegistryOperatorFetchService { } if (toIndex == null || toIndex === -1) { - toIndex = await this.count(overrides); + toIndex = await this.count(moduleAddress, overrides); } const fetcher = async (operatorIndex: number) => { diff --git a/src/common/registry/main/abstract-registry.ts b/src/common/registry/main/abstract-registry.ts index 30fc7c9b..93c543b6 100644 --- a/src/common/registry/main/abstract-registry.ts +++ b/src/common/registry/main/abstract-registry.ts @@ -116,7 +116,7 @@ export abstract class AbstractRegistryService { const blockHash = block.hash; const blockTag = { blockHash }; - const keysOpIndex = await this.metaFetch.fetchKeysOpIndex({ blockTag }, moduleAddress); + const keysOpIndex = await this.metaFetch.fetchKeysOpIndex(moduleAddress, { blockTag }); return { blockNumber: block.number, diff --git a/src/common/registry/storage/operator.entity.ts b/src/common/registry/storage/operator.entity.ts index 59099834..f0a92f2e 100644 --- a/src/common/registry/storage/operator.entity.ts +++ b/src/common/registry/storage/operator.entity.ts @@ -14,6 +14,7 @@ export class RegistryOperator { this.stoppedValidators = operator.stoppedValidators; this.totalSigningKeys = operator.totalSigningKeys; this.usedSigningKeys = operator.usedSigningKeys; + this.moduleAddress = operator.moduleAddress; } @PrimaryKey() @@ -39,4 +40,7 @@ export class RegistryOperator { @Property() usedSigningKeys!: number; + + @Property({ length: 42 }) + moduleAddress!: string; } diff --git a/src/http/common/entities/sr-module.ts b/src/http/common/entities/sr-module.ts index ba0093e1..456bfbc1 100644 --- a/src/http/common/entities/sr-module.ts +++ b/src/http/common/entities/sr-module.ts @@ -1,8 +1,8 @@ import { ApiProperty } from '@nestjs/swagger'; -import { SRModuleEntity } from 'storage/sr-module.entity'; +import { SrModuleEntity } from 'storage/sr-module.entity'; export class SRModule { - constructor(nonce: number, module: SRModuleEntity) { + constructor(nonce: number, module: SrModuleEntity) { this.nonce = nonce; this.type = module.type; this.id = module.id; diff --git a/src/migrations/Migration20230724202500.ts b/src/migrations/Migration20230724202500.ts new file mode 100644 index 00000000..9777f97a --- /dev/null +++ b/src/migrations/Migration20230724202500.ts @@ -0,0 +1,11 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20230724202500 extends Migration { + async up(): Promise { + this.addSql('alter table "registry_operator" add column "module_address" varchar(42) null;'); + } + + async down(): Promise { + this.addSql('alter table "registry_operator" drop column "module_address";'); + } +} diff --git a/src/migrations/Migration20230724204141.ts b/src/migrations/Migration20230724204141.ts new file mode 100644 index 00000000..a3898c6b --- /dev/null +++ b/src/migrations/Migration20230724204141.ts @@ -0,0 +1,31 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20230724204141 extends Migration { + public async up(): Promise { + if (process.env.CHAIN_ID == '1') { + this.addSql("UPDATE registry_operator SET module_address = '0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5'"); + return; + } + + if (process.env.CHAIN_ID == '5') { + this.addSql("UPDATE registry_operator SET module_address = '0x9D4AF1Ee19Dad8857db3a45B0374c81c8A1C6320'"); + return; + } + + //TODO: will have problem on new testnet for example + // maybe better instead of this migration truncate values in db but than update will take more time for holders + // I prefer second way + + throw new Error('CHAIN_ID is wrong, it should be 1 or 5'); + } + + public async down(): Promise { + if (process.env.CHAIN_ID === '1') { + this.addSql('UPDATE registry_operator SET module_address = NULL'); + } else if (process.env.CHAIN_ID === '5') { + this.addSql('UPDATE registry_operator SET module_address = NULL'); + } else { + throw new Error('CHAIN_ID is wrong, it should be 1 or 5'); + } + } +} diff --git a/src/migrations/Migration20230724205529.ts b/src/migrations/Migration20230724205529.ts new file mode 100644 index 00000000..c282f9ea --- /dev/null +++ b/src/migrations/Migration20230724205529.ts @@ -0,0 +1,17 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20230724205529 extends Migration { + async up(): Promise { + this.addSql( + 'alter table "registry_operator" alter column "module_address" type varchar(42) using ("module_address"::varchar(42));', + ); + this.addSql('alter table "registry_operator" alter column "module_address" set not null;'); + } + + async down(): Promise { + this.addSql( + 'alter table "registry_operator" alter column "module_address" type varchar using ("module_address"::varchar);', + ); + this.addSql('alter table "registry_operator" alter column "module_address" drop not null;'); + } +} diff --git a/src/migrations/Migration20230724210022.ts b/src/migrations/Migration20230724210022.ts new file mode 100644 index 00000000..1de9af19 --- /dev/null +++ b/src/migrations/Migration20230724210022.ts @@ -0,0 +1,11 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20230724210022 extends Migration { + public async up(): Promise { + this.addSql('ALTER TABLE "srmodule_entity" RENAME TO "sr_module_entity"'); + } + + public async down(): Promise { + this.addSql('ALTER TABLE "sr_module_entity" RENAME TO "srmodule_entity"'); + } +} diff --git a/src/mikro-orm.config.ts b/src/mikro-orm.config.ts index 92dada57..e872ed3d 100644 --- a/src/mikro-orm.config.ts +++ b/src/mikro-orm.config.ts @@ -8,7 +8,7 @@ import { RegistryMeta, RegistryOperator, RegistryKey } from './common/registry'; import { ConsensusMetaEntity } from '@lido-nestjs/validators-registry'; import { ConsensusValidatorEntity } from '@lido-nestjs/validators-registry'; import { readFileSync } from 'fs'; -import { SRModuleEntity } from './storage/sr-module.entity'; +import { SrModuleEntity } from './storage/sr-module.entity'; import { ElMetaEntity } from './storage/el-meta.entity'; dotenv.config(); @@ -123,7 +123,7 @@ const config: Options = { RegistryMeta, ConsensusValidatorEntity, ConsensusMetaEntity, - SRModuleEntity, + SrModuleEntity, ElMetaEntity, ], migrations: getMigrationOptions(path.join(__dirname, 'migrations'), ['@lido-nestjs/validators-registry']), diff --git a/src/staking-router-modules/staking-router.service.ts b/src/staking-router-modules/staking-router.service.ts index cb05ff28..83d9d1f5 100644 --- a/src/staking-router-modules/staking-router.service.ts +++ b/src/staking-router-modules/staking-router.service.ts @@ -11,7 +11,7 @@ import { KeyWithModuleAddress } from 'http/keys/entities'; import { ELBlockSnapshot, ModuleId } from 'http/common/entities'; import { config } from './staking-module-impl-config'; import { IsolationLevel } from '@mikro-orm/core'; -import { SRModuleEntity } from 'storage/sr-module.entity'; +import { SrModuleEntity } from 'storage/sr-module.entity'; import { SRModuleStorageService } from 'storage/sr-module.storage'; import { ElMetaStorageService } from 'storage/el-meta.storage'; import { ElMetaEntity } from 'storage/el-meta.entity'; @@ -29,12 +29,12 @@ export class StakingRouterService { ) {} // modules list is used in endpoints - public async getStakingModules(): Promise { + public async getStakingModules(): Promise { const srModules = await this.srModulesStorage.findAll(); return srModules; } - public async getStakingModule(moduleId: ModuleId): Promise { + public async getStakingModule(moduleId: ModuleId): Promise { // TODO: here should be more checks if (typeof moduleId === 'number') { return await this.srModulesStorage.findOneById(moduleId); @@ -89,7 +89,7 @@ export class StakingRouterService { // return; // } - // TODO: move to SRModuleEntity storage module + // TODO: move to SrModuleEntity storage module await this.srModulesStorage.store(module, currNonce); // here we already sure that we need to update keys and operators // TODO: next step is removing meta and nonce checking from updateKeys algo in registry implementation diff --git a/src/storage/sr-module.entity.ts b/src/storage/sr-module.entity.ts index b5f36f9d..f5fba93d 100644 --- a/src/storage/sr-module.entity.ts +++ b/src/storage/sr-module.entity.ts @@ -3,7 +3,7 @@ import { StakingModule } from '../staking-router-modules/interfaces'; import { SRModuleRepository } from './sr-module.repository'; @Entity({ customRepository: () => SRModuleRepository }) -export class SRModuleEntity { +export class SrModuleEntity { [EntityRepositoryType]?: SRModuleRepository; [PrimaryKeyType]?: [number, string]; diff --git a/src/storage/sr-module.repository.ts b/src/storage/sr-module.repository.ts index 60a82e4f..eee81e6c 100644 --- a/src/storage/sr-module.repository.ts +++ b/src/storage/sr-module.repository.ts @@ -1,4 +1,4 @@ import { EntityRepository } from '@mikro-orm/knex'; -import { SRModuleEntity } from './sr-module.entity'; +import { SrModuleEntity } from './sr-module.entity'; -export class SRModuleRepository extends EntityRepository {} +export class SRModuleRepository extends EntityRepository {} diff --git a/src/storage/sr-module.storage.ts b/src/storage/sr-module.storage.ts index 022831c9..b4bf83b0 100644 --- a/src/storage/sr-module.storage.ts +++ b/src/storage/sr-module.storage.ts @@ -2,7 +2,7 @@ import { QueryOrder } from '@mikro-orm/core'; import { FilterQuery, FindOptions } from '@mikro-orm/core'; import { Injectable } from '@nestjs/common'; import { StakingModule } from 'staking-router-modules/interfaces'; -import { SRModuleEntity } from './sr-module.entity'; +import { SrModuleEntity } from './sr-module.entity'; import { SRModuleRepository } from './sr-module.repository'; @Injectable() @@ -11,30 +11,30 @@ export class SRModuleStorageService { /** find module */ async find

( - where: FilterQuery, - options?: FindOptions, - ): Promise { + where: FilterQuery, + options?: FindOptions, + ): Promise { return await this.repository.find(where, options); } /** find key by index */ - async findOneById(moduleId: number): Promise { + async findOneById(moduleId: number): Promise { return await this.repository.findOne({ id: moduleId }); } - async findOneByContractAddress(contractAddress: string): Promise { + async findOneByContractAddress(contractAddress: string): Promise { return await this.repository.findOne({ stakingModuleAddress: contractAddress }); } /** find all keys */ - async findAll(): Promise { + async findAll(): Promise { return await this.repository.findAll({ orderBy: [{ id: QueryOrder.ASC }], }); } async store(module: StakingModule, currNonce: number): Promise { - const srModule = new SRModuleEntity(module, currNonce); + const srModule = new SrModuleEntity(module, currNonce); // TODO: what exactly will happen during attempt to write in db module that already exists in db await this.repository .createQueryBuilder() diff --git a/src/storage/storage.module.ts b/src/storage/storage.module.ts index c54364be..e03baa86 100644 --- a/src/storage/storage.module.ts +++ b/src/storage/storage.module.ts @@ -2,14 +2,14 @@ import { MikroOrmModule } from '@mikro-orm/nestjs'; import { Global, Module } from '@nestjs/common'; import { ElMetaEntity } from './el-meta.entity'; import { ElMetaStorageService } from './el-meta.storage'; -import { SRModuleEntity } from './sr-module.entity'; +import { SrModuleEntity } from './sr-module.entity'; import { SRModuleStorageService } from './sr-module.storage'; @Global() @Module({ imports: [ MikroOrmModule.forFeature({ - entities: [SRModuleEntity, ElMetaEntity], + entities: [SrModuleEntity, ElMetaEntity], }), ], providers: [SRModuleStorageService, ElMetaStorageService],