From 265a9634fc748bc3bcd13c9916f1bb3f1c8e2296 Mon Sep 17 00:00:00 2001 From: Michael Xiao Date: Thu, 19 Dec 2024 14:05:14 -0500 Subject: [PATCH] Lombard PoR address --- .changeset/bright-shirts-love.md | 5 + .../src/config/LombardPorAddressList.json | 250 ++++++++++++++++++ .../src/config/{abi.ts => PorAddressList.ts} | 0 .../por-address-list/src/endpoint/address.ts | 7 + .../por-address-list/src/transport/address.ts | 34 ++- .../src/transport/addressManager.ts | 83 ++++++ .../src/transport/multichainAddress.ts | 27 +- .../transport/{utils.ts => providerUtils.ts} | 37 --- .../por-address-list/test/unit/utils.test.ts | 113 +++++--- 9 files changed, 463 insertions(+), 93 deletions(-) create mode 100644 .changeset/bright-shirts-love.md create mode 100644 packages/sources/por-address-list/src/config/LombardPorAddressList.json rename packages/sources/por-address-list/src/config/{abi.ts => PorAddressList.ts} (100%) create mode 100644 packages/sources/por-address-list/src/transport/addressManager.ts rename packages/sources/por-address-list/src/transport/{utils.ts => providerUtils.ts} (53%) diff --git a/.changeset/bright-shirts-love.md b/.changeset/bright-shirts-love.md new file mode 100644 index 0000000000..42ab395894 --- /dev/null +++ b/.changeset/bright-shirts-love.md @@ -0,0 +1,5 @@ +--- +'@chainlink/por-address-list-adapter': minor +--- + +Add Lombard PoR diff --git a/packages/sources/por-address-list/src/config/LombardPorAddressList.json b/packages/sources/por-address-list/src/config/LombardPorAddressList.json new file mode 100644 index 0000000000..c269c68d7f --- /dev/null +++ b/packages/sources/por-address-list/src/config/LombardPorAddressList.json @@ -0,0 +1,250 @@ +[ + { "inputs": [], "name": "AccessControlBadConfirmation", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "bytes32", "name": "neededRole", "type": "bytes32" } + ], + "name": "AccessControlUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [{ "internalType": "string", "name": "addressStr", "type": "string" }], + "name": "AddressAlreadyExists", + "type": "error" + }, + { + "inputs": [{ "internalType": "string", "name": "addressStr", "type": "string" }], + "name": "AddressDoesNotExist", + "type": "error" + }, + { "inputs": [], "name": "ArrayLengthMismatch", "type": "error" }, + { "inputs": [], "name": "InvalidInitialization", "type": "error" }, + { + "inputs": [ + { "internalType": "string", "name": "addressStr", "type": "string" }, + { "internalType": "string", "name": "messageOrPath", "type": "string" }, + { "internalType": "bytes", "name": "signature", "type": "bytes" } + ], + "name": "InvalidMessageSignature", + "type": "error" + }, + { "inputs": [], "name": "InvalidRootPubkey", "type": "error" }, + { + "inputs": [{ "internalType": "bytes32", "name": "id", "type": "bytes32" }], + "name": "InvalidRootPubkeyId", + "type": "error" + }, + { "inputs": [], "name": "NotInitializing", "type": "error" }, + { + "inputs": [{ "internalType": "bytes", "name": "pubkey", "type": "bytes" }], + "name": "RootPubkeyAlreadyExists", + "type": "error" + }, + { "inputs": [], "name": "RootPubkeyCannotBeDeleted", "type": "error" }, + { + "inputs": [{ "internalType": "bytes", "name": "pubkey", "type": "bytes" }], + "name": "RootPubkeyDoesNotExist", + "type": "error" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint64", "name": "version", "type": "uint64" }], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { "indexed": true, "internalType": "bytes32", "name": "newAdminRole", "type": "bytes32" } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPERATOR_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string[]", "name": "_addresses", "type": "string[]" }, + { "internalType": "bytes32[]", "name": "_rootPkIds", "type": "bytes32[]" }, + { "internalType": "string[]", "name": "_messagesOrDerivationData", "type": "string[]" }, + { "internalType": "bytes[]", "name": "_signatures", "type": "bytes[]" } + ], + "name": "addAddresses", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes", "name": "_pubkey", "type": "bytes" }], + "name": "addRootPubkey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "string[]", "name": "_addresses", "type": "string[]" }], + "name": "deleteAddresses", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes", "name": "_pubkey", "type": "bytes" }], + "name": "deleteRootPubkey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getPoRAddressListLength", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_start", "type": "uint256" }, + { "internalType": "uint256", "name": "_end", "type": "uint256" } + ], + "name": "getPoRAddressSignatureMessages", + "outputs": [ + { "internalType": "string[]", "name": "", "type": "string[]" }, + { "internalType": "bytes32[]", "name": "", "type": "bytes32[]" }, + { "internalType": "string[]", "name": "", "type": "string[]" }, + { "internalType": "bytes[]", "name": "", "type": "bytes[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "string[]", "name": "_addresses", "type": "string[]" }], + "name": "getPoRSignatureMessages", + "outputs": [ + { "internalType": "bytes32[]", "name": "", "type": "bytes32[]" }, + { "internalType": "string[]", "name": "", "type": "string[]" }, + { "internalType": "bytes[]", "name": "", "type": "bytes[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "role", "type": "bytes32" }], + "name": "getRoleAdmin", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "_id", "type": "bytes32" }], + "name": "getRootPubkey", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "hasRole", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "_owner", "type": "address" }], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "callerConfirmation", "type": "address" } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string[]", "name": "_addresses", "type": "string[]" }, + { "internalType": "string[]", "name": "_messages", "type": "string[]" }, + { "internalType": "bytes[]", "name": "_signatures", "type": "bytes[]" } + ], + "name": "updateMessageSignature", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/packages/sources/por-address-list/src/config/abi.ts b/packages/sources/por-address-list/src/config/PorAddressList.ts similarity index 100% rename from packages/sources/por-address-list/src/config/abi.ts rename to packages/sources/por-address-list/src/config/PorAddressList.ts diff --git a/packages/sources/por-address-list/src/endpoint/address.ts b/packages/sources/por-address-list/src/endpoint/address.ts index ccf2dbee9e..ede1c50f80 100644 --- a/packages/sources/por-address-list/src/endpoint/address.ts +++ b/packages/sources/por-address-list/src/endpoint/address.ts @@ -44,6 +44,12 @@ export const inputParameters = new InputParameters( type: 'boolean', description: 'Flag to pass on to the balance adapter to search for limbo validators', }, + abiName: { + type: 'string', + options: ['Lombard', 'Default'], + description: 'The name of ABI used for contractAddress', + default: 'Default', + }, }, [ { @@ -53,6 +59,7 @@ export const inputParameters = new InputParameters( batchSize: 10, network: 'ethereum', chainId: '1', + abiName: 'Default', }, ], ) diff --git a/packages/sources/por-address-list/src/transport/address.ts b/packages/sources/por-address-list/src/transport/address.ts index 1641a3de5c..e435f0cda8 100644 --- a/packages/sources/por-address-list/src/transport/address.ts +++ b/packages/sources/por-address-list/src/transport/address.ts @@ -1,12 +1,13 @@ import { SubscriptionTransport } from '@chainlink/external-adapter-framework/transports/abstract/subscription' import { EndpointContext } from '@chainlink/external-adapter-framework/adapter' -import { PoRAddress } from '@chainlink/external-adapter-framework/adapter/por' import { TransportDependencies } from '@chainlink/external-adapter-framework/transports' import { AdapterResponse, sleep } from '@chainlink/external-adapter-framework/util' -import { POR_ADDRESS_LIST_ABI } from '../config/abi' +import { POR_ADDRESS_LIST_ABI } from '../config/PorAddressList' +import LOMBARD_POR_ADDRESS_LIST_ABI from '../config/LombardPorAddressList.json' import { BaseEndpointTypes, inputParameters } from '../endpoint/address' import { ethers } from 'ethers' -import { fetchAddressList, addProvider, getProvider } from './utils' +import { addProvider, getProvider } from './providerUtils' +import { DefaultAddressManager, LombardAddressManager, AddressManager } from './addressManager' export type AddressTransportTypes = BaseEndpointTypes @@ -61,6 +62,8 @@ export class AddressTransport extends SubscriptionTransport> { + const providerDataRequestedUnixMs = Date.now() + const { confirmations, contractAddress, @@ -69,27 +72,34 @@ export class AddressTransport extends SubscriptionTransport + + if (abiName == 'Lombard') { + addressManager = new LombardAddressManager( + contractAddress, + LOMBARD_POR_ADDRESS_LIST_ABI, + provider, + ) + } else { + addressManager = new DefaultAddressManager(contractAddress, POR_ADDRESS_LIST_ABI, provider) + } + const latestBlockNum = await provider.getBlockNumber() - const providerDataRequestedUnixMs = Date.now() - const addressList = await fetchAddressList( - addressManager, + const addressList = await addressManager.fetchAddressList( latestBlockNum, confirmations, batchSize, this.settings.GROUP_SIZE, ) - const addresses: PoRAddress[] = addressList.map((address) => ({ - address, - network, - chainId, - })) + + const addresses = addressManager.processPoRAddressList(addressList, network, chainId) return { data: { diff --git a/packages/sources/por-address-list/src/transport/addressManager.ts b/packages/sources/por-address-list/src/transport/addressManager.ts new file mode 100644 index 0000000000..23f56c642c --- /dev/null +++ b/packages/sources/por-address-list/src/transport/addressManager.ts @@ -0,0 +1,83 @@ +import { ethers } from 'ethers' +import { PoRAddress } from '@chainlink/external-adapter-framework/adapter/por' + +export abstract class AddressManager { + contract: ethers.Contract + + constructor( + contractAddress: string, + abi: ethers.ContractInterface, + provider: ethers.providers.JsonRpcProvider, + ) { + this.contract = new ethers.Contract(contractAddress, abi, provider) + } + + async fetchAddressList( + latestBlockNum: number, + confirmations = 0, + batchSize = 10, + batchGroupSize = 10, + ): Promise { + const blockTag = latestBlockNum - confirmations + const numAddresses = await this.contract.getPoRAddressListLength({ blockTag }) + let totalRequestedAddressesCount = 0 + let startIdx = ethers.BigNumber.from(0) + const addresses: T[] = [] + let batchRequests: Promise[] = [] + + while (totalRequestedAddressesCount < numAddresses.toNumber()) { + const nextEndIdx = startIdx.add(batchSize) + const endIdx = nextEndIdx.gte(numAddresses) ? numAddresses.sub(1) : nextEndIdx + const batchCall = this.getPoRAddressListCall(startIdx, endIdx, blockTag) + batchRequests.push(batchCall) + // element at endIdx is included in result + const addressesRequested: number = endIdx.sub(startIdx).add(1).toNumber() + totalRequestedAddressesCount += addressesRequested + startIdx = endIdx.add(1) + + if ( + batchRequests.length >= batchGroupSize || + totalRequestedAddressesCount >= numAddresses.toNumber() + ) { + addresses.push(...(await Promise.all(batchRequests))) + batchRequests = [] + } + } + return addresses + } + + abstract getPoRAddressListCall(start: ethers.BigNumber, end: number, blockTag: number): Promise + + abstract processPoRAddressList(result: T[], network: string, chainId: string): PoRAddress[] +} + +export class DefaultAddressManager extends AddressManager { + getPoRAddressListCall(start: ethers.BigNumber, end: number, blockTag: number) { + return this.contract.getPoRAddressList(start, end, { blockTag }) + } + + processPoRAddressList(result: string[][], network: string, chainId: string) { + return result.flat().map((address) => ({ + address, + network, + chainId, + })) + } +} + +export class LombardAddressManager extends AddressManager { + getPoRAddressListCall(start: ethers.BigNumber, end: number, blockTag: number) { + return this.contract.getPoRAddressSignatureMessages(start.toNumber(), end, { blockTag }) + } + + processPoRAddressList(result: string[][][], network: string, chainId: string) { + return result + .flatMap((r) => r[0]) + .filter((address) => address != '') + .map((address) => ({ + address: address.replace("'", ''), + network: network, + chainId: chainId, + })) + } +} diff --git a/packages/sources/por-address-list/src/transport/multichainAddress.ts b/packages/sources/por-address-list/src/transport/multichainAddress.ts index af0255c7ba..66af5846a3 100644 --- a/packages/sources/por-address-list/src/transport/multichainAddress.ts +++ b/packages/sources/por-address-list/src/transport/multichainAddress.ts @@ -7,8 +7,9 @@ import SolvMultiAddressListABI from '../config/SolvMultiAddressList.json' import MultiEVMPoRAddressListABI from '../config/MultiEVMPoRAddressList.json' import { BaseEndpointTypes, inputParameters } from '../endpoint/multichainAddress' import { ethers } from 'ethers' -import { fetchAddressList, addProvider, getProvider } from './utils' +import { addProvider, getProvider } from './providerUtils' import { AdapterInputError } from '@chainlink/external-adapter-framework/validation/error' +import { AddressManager } from './addressManager' export type AddressTransportTypes = BaseEndpointTypes @@ -72,12 +73,11 @@ export class AddressTransport extends SubscriptionTransport( - addressManager, + const addressList = await addressManager.fetchAddressList( latestBlockNum, confirmations, batchSize, @@ -128,9 +128,21 @@ export class AddressTransport extends SubscriptionTransport { +class MultiAddressManager extends AddressManager { + getPoRAddressListCall(start: ethers.BigNumber, end: number, blockTag: number) { + return this.contract.getPoRAddressList(start, end, { blockTag }) + } + + // Method not used here + // eslint-disable-next-line @typescript-eslint/no-unused-vars + processPoRAddressList(_: ResponseSchema[][], __: string, ___: string) { + return [] + } +} + +const buildTokenResponse = (addressList: ResponseSchema[][], vaultPlaceHolder?: string) => { const addressByChain = Map.groupBy( - addressList.filter((addr) => addr.tokenAddress != vaultPlaceHolder), + addressList.flat().filter((addr) => addr.tokenAddress != vaultPlaceHolder), (address) => address.chainId.toString() + address.tokenAddress, ) @@ -148,8 +160,9 @@ const buildTokenResponse = (addressList: ResponseSchema[], vaultPlaceHolder?: st ).sort() } -const buildVaultResponse = (addressList: ResponseSchema[], vaultPlaceHolder?: string) => { +const buildVaultResponse = (addressList: ResponseSchema[][], vaultPlaceHolder?: string) => { return addressList + .flat() .filter((addr) => addr.tokenAddress == vaultPlaceHolder) .map((addr) => ({ address: addr.vaultAddress, diff --git a/packages/sources/por-address-list/src/transport/utils.ts b/packages/sources/por-address-list/src/transport/providerUtils.ts similarity index 53% rename from packages/sources/por-address-list/src/transport/utils.ts rename to packages/sources/por-address-list/src/transport/providerUtils.ts index 378ae25945..2d23d6191f 100644 --- a/packages/sources/por-address-list/src/transport/utils.ts +++ b/packages/sources/por-address-list/src/transport/providerUtils.ts @@ -4,43 +4,6 @@ import { AdapterInputError } from '@chainlink/external-adapter-framework/validat const logger = makeLogger('utils') -export const fetchAddressList = async ( - addressManager: ethers.Contract, - latestBlockNum: number, - confirmations = 0, - batchSize = 10, - batchGroupSize = 10, -): Promise => { - const blockTag = latestBlockNum - confirmations - const numAddresses = await addressManager.getPoRAddressListLength({ - blockTag, - }) - let totalRequestedAddressesCount = 0 - let startIdx = ethers.BigNumber.from(0) - const addresses: T[] = [] - let batchRequests: Promise[] = [] - - while (totalRequestedAddressesCount < numAddresses.toNumber()) { - const nextEndIdx = startIdx.add(batchSize) - const endIdx = nextEndIdx.gte(numAddresses) ? numAddresses.sub(1) : nextEndIdx - const batchCall = addressManager.getPoRAddressList(startIdx, endIdx, { blockTag }) - batchRequests.push(batchCall) - // element at endIdx is included in result - const addressesRequested: number = endIdx.sub(startIdx).add(1).toNumber() - totalRequestedAddressesCount += addressesRequested - startIdx = endIdx.add(1) - - if ( - batchRequests.length >= batchGroupSize || - totalRequestedAddressesCount >= numAddresses.toNumber() - ) { - addresses.push(...(await Promise.all(batchRequests)).flat()) - batchRequests = [] - } - } - return addresses -} - export const addProvider = ( network: string, providers: Record, diff --git a/packages/sources/por-address-list/test/unit/utils.test.ts b/packages/sources/por-address-list/test/unit/utils.test.ts index 14e7dff6ef..98416bfe93 100644 --- a/packages/sources/por-address-list/test/unit/utils.test.ts +++ b/packages/sources/por-address-list/test/unit/utils.test.ts @@ -1,5 +1,5 @@ import { ethers } from 'ethers' -import { fetchAddressList } from '../../src/transport/utils' +import { DefaultAddressManager } from '../../src/transport/addressManager' const DEFAULT_EXPECTED_ADDRESSES = [ '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', @@ -16,36 +16,56 @@ const DEFAULT_EXPECTED_ADDRESSES = [ const LATEST_BLOCK_NUM = 1000 -const AddressManagerMock: jest.Mock = jest - .fn() - .mockImplementation((expectedAddresses?: string[]) => { - const addresses = expectedAddresses || DEFAULT_EXPECTED_ADDRESSES - return { - getPoRAddressListLength: jest.fn().mockReturnValue(ethers.BigNumber.from(addresses.length)), - getPoRAddressList: jest - .fn() - .mockImplementation((startIdx: ethers.BigNumber, endIdx: ethers.BigNumber) => { - const lastIdx = endIdx.gte(ethers.BigNumber.from(addresses.length)) - ? addresses.length - 1 - : endIdx.toNumber() - return addresses.slice(startIdx.toNumber(), lastIdx + 1) - }), - } - }) +jest.mock('ethers', () => { + const originalModule = jest.requireActual('ethers') + + return { + ...originalModule, + ethers: { + ...originalModule.ethers, + providers: { + JsonRpcProvider: function (): ethers.providers.JsonRpcProvider { + return {} as ethers.providers.JsonRpcBatchProvider + }, + }, + Contract: jest.fn().mockImplementation(() => ({ + getPoRAddressListLength: jest + .fn() + .mockReturnValue(originalModule.BigNumber.from(DEFAULT_EXPECTED_ADDRESSES.length)), + getPoRAddressList: jest + .fn() + .mockImplementation((startIdx: ethers.BigNumber, endIdx: ethers.BigNumber) => { + const lastIdx = endIdx.gte( + originalModule.BigNumber.from(DEFAULT_EXPECTED_ADDRESSES.length), + ) + ? DEFAULT_EXPECTED_ADDRESSES.length - 1 + : endIdx.toNumber() + return DEFAULT_EXPECTED_ADDRESSES.slice(startIdx.toNumber(), lastIdx + 1) + }), + })), + }, + } +}) describe('address endpoint', () => { describe('#fetchAddressList', () => { describe('confirmations', () => { it('reads from the latest block if confirmations is 0', async () => { - const addressManager = new AddressManagerMock() + const addressManager = new DefaultAddressManager( + '', + [], + new ethers.providers.JsonRpcProvider(), + ) + const confirmations = 0 const batchSize = DEFAULT_EXPECTED_ADDRESSES.length - await fetchAddressList(addressManager, LATEST_BLOCK_NUM, confirmations, batchSize) - expect(addressManager.getPoRAddressListLength).toHaveBeenCalledWith({ + await addressManager.fetchAddressList(LATEST_BLOCK_NUM, confirmations, batchSize) + + expect(addressManager.contract.getPoRAddressListLength).toHaveBeenCalledWith({ blockTag: LATEST_BLOCK_NUM, }) - expect(addressManager.getPoRAddressList).toHaveBeenCalledWith( + expect(addressManager.contract.getPoRAddressList).toHaveBeenCalledWith( ethers.BigNumber.from(0), ethers.BigNumber.from(DEFAULT_EXPECTED_ADDRESSES.length - 1), { blockTag: LATEST_BLOCK_NUM }, @@ -53,14 +73,21 @@ describe('address endpoint', () => { }) it('uses the correct number of confirmations if it is greater than 0', async () => { - const addressManager = new AddressManagerMock() + const addressManager = new DefaultAddressManager( + '', + [], + new ethers.providers.JsonRpcProvider(), + ) + const confirmations = 2 const batchSize = DEFAULT_EXPECTED_ADDRESSES.length - await fetchAddressList(addressManager, LATEST_BLOCK_NUM, confirmations, batchSize) - expect(addressManager.getPoRAddressListLength).toHaveBeenCalledWith({ + + await addressManager.fetchAddressList(LATEST_BLOCK_NUM, confirmations, batchSize) + + expect(addressManager.contract.getPoRAddressListLength).toHaveBeenCalledWith({ blockTag: LATEST_BLOCK_NUM - confirmations, }) - expect(addressManager.getPoRAddressList).toHaveBeenCalledWith( + expect(addressManager.contract.getPoRAddressList).toHaveBeenCalledWith( ethers.BigNumber.from(0), ethers.BigNumber.from(DEFAULT_EXPECTED_ADDRESSES.length - 1), { blockTag: LATEST_BLOCK_NUM - confirmations }, @@ -69,43 +96,55 @@ describe('address endpoint', () => { }) it('fetches the whole address list if the batch size is greater than the size of the address list', async () => { - const addressManager = new AddressManagerMock() + const addressManager = new DefaultAddressManager( + '', + [], + new ethers.providers.JsonRpcProvider(), + ) + const confirmations = 0 const batchSize = DEFAULT_EXPECTED_ADDRESSES.length * 2 - const result = await fetchAddressList( - addressManager, + + const result = await addressManager.fetchAddressList( LATEST_BLOCK_NUM, confirmations, batchSize, ) + verifyAddressListMatches(result, DEFAULT_EXPECTED_ADDRESSES) }) it('fetches the whole address list if the batch size is smaller than the size of the address list', async () => { - const addressManager = new AddressManagerMock() + const addressManager = new DefaultAddressManager( + '', + [], + new ethers.providers.JsonRpcProvider(), + ) + const confirmations = 0 const batchSize = 3 - const result = await fetchAddressList( - addressManager, + + const result = await addressManager.fetchAddressList( LATEST_BLOCK_NUM, confirmations, batchSize, ) + verifyAddressListMatches(result, DEFAULT_EXPECTED_ADDRESSES) - expect(addressManager.getPoRAddressList).toHaveBeenNthCalledWith( + expect(addressManager.contract.getPoRAddressList).toHaveBeenNthCalledWith( 1, ethers.BigNumber.from(0), ethers.BigNumber.from(3), { blockTag: LATEST_BLOCK_NUM - confirmations }, ) - expect(addressManager.getPoRAddressList).toHaveBeenNthCalledWith( + expect(addressManager.contract.getPoRAddressList).toHaveBeenNthCalledWith( 2, ethers.BigNumber.from(4), ethers.BigNumber.from(7), { blockTag: LATEST_BLOCK_NUM - confirmations }, ) - expect(addressManager.getPoRAddressList).toHaveBeenNthCalledWith( + expect(addressManager.contract.getPoRAddressList).toHaveBeenNthCalledWith( 3, ethers.BigNumber.from(8), ethers.BigNumber.from(DEFAULT_EXPECTED_ADDRESSES.length - 1), @@ -115,9 +154,9 @@ describe('address endpoint', () => { }) }) -const verifyAddressListMatches = (actual: string[], expected: string[]) => { - expect(actual.length).toEqual(DEFAULT_EXPECTED_ADDRESSES.length) +const verifyAddressListMatches = (actual: string[][], expected: string[]) => { + expect(actual.flat().length).toEqual(DEFAULT_EXPECTED_ADDRESSES.length) for (let i = 0; i < DEFAULT_EXPECTED_ADDRESSES.length; i++) { - expect(actual[i]).toEqual(expected[i]) + expect(actual.flat()[i]).toEqual(expected[i]) } }