diff --git a/src/common/io.ts b/src/common/io.ts index 91f49a85..8eaecdd3 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -45,6 +45,7 @@ import { AoEpochData, AoEpochSettings, AoGateway, + AoGatewayDelegate, AoIORead, AoIOWrite, AoRegistrationFees, @@ -313,6 +314,54 @@ export class IOReadable implements AoIORead { }); } + async getGatewayDelegates( + params?: { address: WalletAddress } & PaginationParams, + ): Promise> { + const allTags = [ + { name: 'Action', value: 'Paginated-Delegates' }, + { name: 'Address', value: params?.address }, + { name: 'Cursor', value: params?.cursor?.toString() }, + { name: 'Limit', value: params?.limit?.toString() }, + { name: 'Sort-By', value: params?.sortBy }, + { name: 'Sort-Order', value: params?.sortOrder }, + ]; + + const prunedTags: { name: string; value: string }[] = allTags.filter( + (tag: { + name: string; + value: string | undefined; + }): tag is { name: string; value: string } => tag.value !== undefined, + ); + + return this.process.read>({ + tags: prunedTags, + }); + } + + async getGatewayDelegateAllowList( + params?: Omit & { address: WalletAddress }, + ): Promise> { + const allTags = [ + { name: 'Action', value: 'Paginated-Allowed-Delegates' }, + { name: 'Address', value: params?.address }, + { name: 'Cursor', value: params?.cursor?.toString() }, + { name: 'Limit', value: params?.limit?.toString() }, + { name: 'Sort-Order', value: params?.sortOrder }, + // note: sortBy is omitted because it's not supported for this action as table is an of addresses + ]; + + const prunedTags: { name: string; value: string }[] = allTags.filter( + (tag: { + name: string; + value: string | undefined; + }): tag is { name: string; value: string } => tag.value !== undefined, + ); + + return this.process.read>({ + tags: prunedTags, + }); + } + async getGateways( pageParams?: PaginationParams, ): Promise> { @@ -751,6 +800,7 @@ export class IOWriteable extends IOReadable implements AoIOWrite { { operatorStake, allowDelegatedStaking, + allowedDelegates, delegateRewardShareRatio, fqdn, label, @@ -776,6 +826,10 @@ export class IOWriteable extends IOReadable implements AoIOWrite { name: 'Allow-Delegated-Staking', value: allowDelegatedStaking?.toString(), }, + { + name: 'Allowed-Delegates', + value: allowedDelegates?.join(','), + }, { name: 'Delegate-Reward-Share-Ratio', value: delegateRewardShareRatio?.toString(), diff --git a/src/types/io.ts b/src/types/io.ts index 86140f15..96c9219e 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -40,7 +40,7 @@ export type PaginationResult = { items: T[]; nextCursor: string | undefined; totalItems: number; - sortBy: keyof T; + sortBy?: keyof T; sortOrder: 'asc' | 'desc'; hasMore: boolean; }; @@ -188,10 +188,12 @@ export type AoGatewayServices = } | undefined; // not required, for now +export type AoGatewayDelegates = Record; +export type AoGatewayDelegateAllowList = WalletAddress[]; + export type AoGateway = { settings: AoGatewaySettings; stats: AoGatewayStats; - delegates: Record; totalDelegatedStake: number; vaults: Record; startTimestamp: Timestamp; @@ -240,8 +242,9 @@ export type AoGatewayDelegate = { }; export type AoGatewaySettings = { - allowDelegatedStaking: boolean; + allowDelegatedStaking: boolean | 'allowlist'; delegateRewardShareRatio: number; + allowedDelegates: WalletAddress[]; minDelegatedStake: number; autoStake: boolean; label: string; @@ -312,6 +315,14 @@ export interface AoIORead { }: { address: WalletAddress; }): Promise; + // TODO: these could be moved to a separate Gateways class that implements gateway specific interactions + getGatewayDelegates( + params?: PaginationParams, + ): Promise>; + getGatewayDelegateAllowList( + params?: PaginationParams, + ): Promise>; + // END OF GATEWAY SPECIFIC INTERACTIONS getGateways( params?: PaginationParams, ): Promise>; @@ -387,38 +398,14 @@ export interface AoIOWrite extends AoIORead { }, options?: WriteOptions, ): Promise; + // TODO: these could be moved to a separate Gateways class that implements gateway specific interactions joinNetwork( - { - operatorStake, - allowDelegatedStaking, - delegateRewardShareRatio, - fqdn, - label, - minDelegatedStake, - note, - port, - properties, - protocol, - autoStake, - observerAddress, - }: AoJoinNetworkParams, + params: AoJoinNetworkParams, options?: WriteOptions, ): Promise; leaveNetwork(options?: WriteOptions): Promise; updateGatewaySettings( - { - allowDelegatedStaking, - delegateRewardShareRatio, - fqdn, - label, - minDelegatedStake, - note, - port, - properties, - protocol, - autoStake, - observerAddress, - }: AoUpdateGatewaySettingsParams, + params: AoUpdateGatewaySettingsParams, options?: WriteOptions, ): Promise; increaseOperatorStake( @@ -462,6 +449,7 @@ export interface AoIOWrite extends AoIORead { }, options?: WriteOptions, ): Promise; + // END OF GATEWAY SPECIFIC INTERACTIONS buyRecord( params: { name: string; diff --git a/tests/e2e/esm/index.test.js b/tests/e2e/esm/index.test.js index 28b7de87..137e0e15 100644 --- a/tests/e2e/esm/index.test.js +++ b/tests/e2e/esm/index.test.js @@ -154,6 +154,12 @@ describe('e2e esm tests', async () => { assert(typeof gateway.weights.tenureWeight === 'number'); assert(typeof gateway.weights.observerRewardRatioWeight === 'number'); assert(typeof gateway.weights.gatewayRewardRatioWeight === 'number'); + if (gateway.vaults?.length > 0) { + gateway.vaults.forEach((vault) => { + assert(typeof vault.balance === 'number'); + assert(typeof vault.startTimestamp === 'number'); + }); + } }); }); @@ -191,14 +197,71 @@ describe('e2e esm tests', async () => { assert(typeof gateway.weights.tenureWeight === 'number'); assert(typeof gateway.weights.observerRewardRatioWeight === 'number'); assert(typeof gateway.weights.gatewayRewardRatioWeight === 'number'); + if (gateway.vaults?.length > 0) { + gateway.vaults.forEach((vault) => { + assert(typeof vault.balance === 'number'); + assert(typeof vault.startTimestamp === 'number'); + }); + } }); }); it('should be able to get a single gateway', async () => { - const gateways = await io.getGateway({ + const gateway = await io.getGateway({ address: 'QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ', }); - assert.ok(gateways); + assert.ok(gateway); + }); + + it('should be able to get gateway delegates', async () => { + const delegates = await io.getGatewayDelegates({ + address: 'QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ', + limit: 1, + sortBy: 'startTimestamp', + sortOrder: 'desc', + }); + assert.ok(delegates); + assert(delegates.limit === 1); + assert(delegates.sortOrder === 'desc'); + assert(delegates.sortBy === 'startTimestamp'); + assert(typeof delegates.totalItems === 'number'); + assert(typeof delegates.sortBy === 'string'); + assert(typeof delegates.sortOrder === 'string'); + assert(typeof delegates.limit === 'number'); + assert(typeof delegates.hasMore === 'boolean'); + if (delegates.nextCursor) { + assert(typeof delegates.nextCursor === 'string'); + } + assert(Array.isArray(delegates.items)); + delegates.items.forEach((delegate) => { + assert(Array.isArray(delegate.vaults)); + assert(typeof delegate.delegatedStake === 'number'); + assert(typeof delegate.startTimestamp === 'number'); + }); + }); + + it('should be able to get gateway delegate allow list', async () => { + const allowList = await io.getGatewayDelegateAllowList({ + address: 'QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ', + limit: 1, + sortBy: 'startTimestamp', + sortOrder: 'desc', + }); + assert.ok(allowList); + // note: sortBy is omitted because it's not supported for the contract handler + assert(allowList.limit === 1); + assert(allowList.sortOrder === 'desc'); + assert(typeof allowList.totalItems === 'number'); + assert(typeof allowList.sortOrder === 'string'); + assert(typeof allowList.limit === 'number'); + assert(typeof allowList.hasMore === 'boolean'); + if (allowList.nextCursor) { + assert(typeof allowList.nextCursor === 'string'); + } + assert(Array.isArray(allowList.items)); + allowList.items.forEach((address) => { + assert(typeof address === 'string'); + }); }); it('should be able to get balances, defaulting to first page', async () => {