From ebfd6817a6f36e179e3729c781dad65d71b6b4e5 Mon Sep 17 00:00:00 2001 From: Bhavi Dhingra Date: Wed, 25 Mar 2026 00:12:05 +0530 Subject: [PATCH 1/2] feat(express): add typed routes for TRX delegation APIs Add dedicated Express routes for accountresources and activedelegations endpoints with io-ts validation. TICKET: CHALO-346 --- modules/express/src/clientRoutes.ts | 44 +++++++++ modules/express/src/typedRoutes/api/index.ts | 18 ++++ .../typedRoutes/api/v2/accountResources.ts | 95 +++++++++++++++++++ .../typedRoutes/api/v2/resourceDelegations.ts | 82 ++++++++++++++++ modules/sdk-core/src/bitgo/wallet/wallet.ts | 6 +- .../unit/bitgo/wallet/getAccountResources.ts | 36 +++---- 6 files changed, 260 insertions(+), 21 deletions(-) create mode 100644 modules/express/src/typedRoutes/api/v2/accountResources.ts create mode 100644 modules/express/src/typedRoutes/api/v2/resourceDelegations.ts diff --git a/modules/express/src/clientRoutes.ts b/modules/express/src/clientRoutes.ts index 8e6ee42e63..25e3947832 100755 --- a/modules/express/src/clientRoutes.ts +++ b/modules/express/src/clientRoutes.ts @@ -1012,6 +1012,40 @@ async function handleV2SendMany(req: ExpressApiRouteRequest<'express.v2.wallet.s return result; } +/** + * handle get account resources + * @param req + */ +async function handleV2AccountResources(req: ExpressApiRouteRequest<'express.v2.wallet.getaccountresources', 'post'>) { + const bitgo = req.bitgo; + const coin = bitgo.coin(req.decoded.coin); + const wallet = await coin.wallets().get({ id: req.decoded.id }); + return wallet.getAccountResources({ + addresses: req.decoded.addresses, + destinationAddress: req.decoded.destinationAddress, + }); +} + +/** + * handle get resource delegations + * @param req + */ +async function handleV2ResourceDelegations( + req: ExpressApiRouteRequest<'express.v2.wallet.resourcedelegations', 'get'> +) { + const bitgo = req.bitgo; + const coin = req.decoded.coin; + const walletId = req.decoded.id; + const query: Record = {}; + if (req.decoded.type) query.type = req.decoded.type; + if (req.decoded.resource) query.resource = req.decoded.resource; + if (req.decoded.limit) query.limit = req.decoded.limit; + return bitgo + .get(bitgo.url(`/${coin}/wallet/${walletId}/resourcedelegations`, 2)) + .query(query) + .result(); +} + /** * payload meant for prebuildAndSignTransaction() in sdk-core which * validates the payload and makes the appropriate request to WP to @@ -1770,6 +1804,16 @@ export function setupAPIRoutes(app: express.Application, config: Config): void { typedPromiseWrapper(handleV2ConsolidateAccount), ]); + // TRX resource delegation + router.post('express.v2.wallet.getaccountresources', [ + prepareBitGo(config), + typedPromiseWrapper(handleV2AccountResources), + ]); + router.get('express.v2.wallet.resourcedelegations', [ + prepareBitGo(config), + typedPromiseWrapper(handleV2ResourceDelegations), + ]); + // Miscellaneous router.post('express.canonicaladdress', [prepareBitGo(config), typedPromiseWrapper(handleCanonicalAddress)]); router.post('express.verifycoinaddress', [prepareBitGo(config), typedPromiseWrapper(handleV2VerifyAddress)]); diff --git a/modules/express/src/typedRoutes/api/index.ts b/modules/express/src/typedRoutes/api/index.ts index 74b16957d9..f16bb76e42 100644 --- a/modules/express/src/typedRoutes/api/index.ts +++ b/modules/express/src/typedRoutes/api/index.ts @@ -51,6 +51,8 @@ import { PostWalletEnableTokens } from './v2/walletEnableTokens'; import { PostWalletSweep } from './v2/walletSweep'; import { PostWalletAccelerateTx } from './v2/walletAccelerateTx'; import { PostIsWalletAddress } from './v2/isWalletAddress'; +import { GetAccountResources } from './v2/accountResources'; +import { GetResourceDelegations } from './v2/resourceDelegations'; // Too large types can cause the following error // @@ -322,6 +324,18 @@ export const ExpressV2WalletAccelerateTxApiSpec = apiSpec({ }, }); +export const ExpressV2WalletAccountResourcesApiSpec = apiSpec({ + 'express.v2.wallet.getaccountresources': { + post: GetAccountResources, + }, +}); + +export const ExpressV2WalletResourceDelegationsApiSpec = apiSpec({ + 'express.v2.wallet.resourcedelegations': { + get: GetResourceDelegations, + }, +}); + export type ExpressApi = typeof ExpressPingApiSpec & typeof ExpressPingExpressApiSpec & typeof ExpressLoginApiSpec & @@ -360,6 +374,8 @@ export type ExpressApi = typeof ExpressPingApiSpec & typeof ExpressV2CanonicalAddressApiSpec & typeof ExpressV2WalletSweepApiSpec & typeof ExpressV2WalletAccelerateTxApiSpec & + typeof ExpressV2WalletAccountResourcesApiSpec & + typeof ExpressV2WalletResourceDelegationsApiSpec & typeof ExpressWalletManagementApiSpec; export const ExpressApi: ExpressApi = { @@ -401,6 +417,8 @@ export const ExpressApi: ExpressApi = { ...ExpressV2CanonicalAddressApiSpec, ...ExpressV2WalletSweepApiSpec, ...ExpressV2WalletAccelerateTxApiSpec, + ...ExpressV2WalletAccountResourcesApiSpec, + ...ExpressV2WalletResourceDelegationsApiSpec, ...ExpressWalletManagementApiSpec, }; diff --git a/modules/express/src/typedRoutes/api/v2/accountResources.ts b/modules/express/src/typedRoutes/api/v2/accountResources.ts new file mode 100644 index 0000000000..6c3da9a9eb --- /dev/null +++ b/modules/express/src/typedRoutes/api/v2/accountResources.ts @@ -0,0 +1,95 @@ +import * as t from 'io-ts'; +import { httpRoute, httpRequest, optional } from '@api-ts/io-ts-http'; +import { BitgoExpressError } from '../../schemas/error'; + +/** + * Path parameters for account resources endpoint + */ +export const AccountResourcesParams = { + /** Coin identifier (e.g., 'trx', 'ttrx') */ + coin: t.string, + /** Wallet ID */ + id: t.string, +} as const; + +/** + * Body parameters for account resources endpoint + */ +export const AccountResourcesBody = { + /** On-chain addresses to query resources for */ + addresses: t.array(t.string), + /** Optional destination address to calculate energy deficit for token transfers */ + destinationAddress: optional(t.string), +} as const; + +/** + * Account resource information for a single address + */ +export const AccountResourceInfo = t.intersection([ + t.type({ + address: t.string, + free_bandwidth_available: t.number, + free_bandwidth_used: t.number, + staked_bandwidth_available: t.number, + staked_bandwidth_used: t.number, + energy_available: t.number, + energy_used: t.number, + resourceDeficitForAssetTransfer: t.intersection([ + t.type({ + bandwidthDeficit: t.number, + bandwidthSunRequired: t.string, + }), + t.partial({ + energyDeficit: t.number, + energySunRequired: t.string, + }), + ]), + }), + t.partial({ + maxResourcesDelegatable: t.type({ + bandwidthSun: t.string, + energySun: t.string, + }), + }), +]); + +/** + * Failed address information + */ +export const FailedAddressInfo = t.type({ + address: t.string, + error: t.string, +}); + +/** + * Response for account resources + */ +export const AccountResourcesResponse = { + /** Account resources for the queried addresses */ + 200: t.type({ + resources: t.array(AccountResourceInfo), + failedAddresses: t.array(FailedAddressInfo), + }), + /** Invalid request */ + 400: BitgoExpressError, +} as const; + +/** + * Get Account Resources + * + * Query BANDWIDTH and ENERGY resource information for TRX wallet addresses. + * Returns resource availability, usage, and optional deficit calculations + * for token transfers. + * + * @operationId express.v2.wallet.getaccountresources + * @tag express + */ +export const GetAccountResources = httpRoute({ + path: '/api/v2/{coin}/wallet/{id}/getaccountresources', + method: 'POST', + request: httpRequest({ + params: AccountResourcesParams, + body: AccountResourcesBody, + }), + response: AccountResourcesResponse, +}); diff --git a/modules/express/src/typedRoutes/api/v2/resourceDelegations.ts b/modules/express/src/typedRoutes/api/v2/resourceDelegations.ts new file mode 100644 index 0000000000..d688487a31 --- /dev/null +++ b/modules/express/src/typedRoutes/api/v2/resourceDelegations.ts @@ -0,0 +1,82 @@ +import * as t from 'io-ts'; +import { NumberFromString } from 'io-ts-types'; +import { httpRoute, httpRequest, optional } from '@api-ts/io-ts-http'; +import { BitgoExpressError } from '../../schemas/error'; + +/** + * Path parameters for resource delegations endpoint + */ +export const ResourceDelegationsParams = { + /** Coin identifier (e.g., 'trx', 'ttrx') */ + coin: t.string, + /** Wallet ID */ + id: t.string, +} as const; + +/** + * Query parameters for resource delegations endpoint + */ +export const ResourceDelegationsQuery = { + /** Filter by delegation direction: 'outgoing' for delegations from this address, 'incoming' for delegations to this address; omit to fetch both */ + type: optional(t.union([t.literal('outgoing'), t.literal('incoming')])), + /** Filter by resource type (case-insensitive: ENERGY, energy, BANDWIDTH, bandwidth) */ + resource: optional(t.string), + /** Maximum number of results to return */ + limit: optional(NumberFromString), + /** Pagination cursor from previous response */ + nextBatchPrevId: optional(t.string), +} as const; + +/** + * A single delegation record + */ +export const DelegationRecord = t.type({ + id: t.string, + coin: t.string, + ownerAddress: t.string, + receiverAddress: t.string, + resource: t.union([t.literal('ENERGY'), t.literal('BANDWIDTH')]), + balance: t.string, + updatedAt: t.string, +}); + +/** + * Response for resource delegations + */ +export const ResourceDelegationsResponse = { + /** Resource delegations for the wallet */ + 200: t.type({ + address: t.string, + coin: t.string, + delegations: t.intersection([ + t.type({ + outgoing: t.array(DelegationRecord), + incoming: t.array(DelegationRecord), + }), + t.partial({ + nextBatchPrevId: t.string, + }), + ]), + }), + /** Invalid request */ + 400: BitgoExpressError, +} as const; + +/** + * Get Resource Delegations + * + * Query active outgoing and incoming ENERGY/BANDWIDTH resource delegations + * for a TRX wallet. + * + * @operationId express.v2.wallet.resourcedelegations + * @tag express + */ +export const GetResourceDelegations = httpRoute({ + path: '/api/v2/{coin}/wallet/{id}/resourcedelegations', + method: 'GET', + request: httpRequest({ + params: ResourceDelegationsParams, + query: ResourceDelegationsQuery, + }), + response: ResourceDelegationsResponse, +}); diff --git a/modules/sdk-core/src/bitgo/wallet/wallet.ts b/modules/sdk-core/src/bitgo/wallet/wallet.ts index 026e41b5dd..85ef4a998d 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallet.ts @@ -1483,12 +1483,12 @@ export class Wallet implements IWallet { throw new Error('addresses array cannot be empty'); } - const query: GetAccountResourcesOptions = { addresses }; + const body: GetAccountResourcesOptions = { addresses }; if (destinationAddress) { - query.destinationAddress = destinationAddress; + body.destinationAddress = destinationAddress; } - return this.bitgo.get(this.url('/getAccountResources')).query(query).result(); + return this.bitgo.post(this.url('/getAccountResources')).send(body).result(); } async updateWalletBuildDefaults(params: UpdateBuildDefaultOptions): Promise { diff --git a/modules/sdk-core/test/unit/bitgo/wallet/getAccountResources.ts b/modules/sdk-core/test/unit/bitgo/wallet/getAccountResources.ts index 56d3c646aa..80a3caddbc 100644 --- a/modules/sdk-core/test/unit/bitgo/wallet/getAccountResources.ts +++ b/modules/sdk-core/test/unit/bitgo/wallet/getAccountResources.ts @@ -11,7 +11,7 @@ describe('Wallet - getAccountResources', function () { beforeEach(function () { mockBitGo = { - get: sinon.stub(), + post: sinon.stub(), }; mockBaseCoin = { @@ -40,8 +40,8 @@ describe('Wallet - getAccountResources', function () { ], }; - mockBitGo.get.returns({ - query: sinon.stub().returns({ + mockBitGo.post.returns({ + send: sinon.stub().returns({ result: sinon.stub().resolves(mockResponse), }), }); @@ -50,9 +50,9 @@ describe('Wallet - getAccountResources', function () { const result = await wallet.getAccountResources({ addresses }); result.should.deepEqual(mockResponse); - sinon.assert.calledOnce(mockBitGo.get); - const queryStub = mockBitGo.get.returnValues[0].query; - sinon.assert.calledWith(queryStub, { addresses }); + sinon.assert.calledOnce(mockBitGo.post); + const sendStub = mockBitGo.post.returnValues[0].send; + sinon.assert.calledWith(sendStub, { addresses }); }); it('should call WP API with addresses and destinationAddress parameters', async function () { @@ -60,8 +60,8 @@ describe('Wallet - getAccountResources', function () { resources: [{ address: 'address1', balance: 100 }], }; - mockBitGo.get.returns({ - query: sinon.stub().returns({ + mockBitGo.post.returns({ + send: sinon.stub().returns({ result: sinon.stub().resolves(mockResponse), }), }); @@ -71,9 +71,9 @@ describe('Wallet - getAccountResources', function () { const result = await wallet.getAccountResources({ addresses, destinationAddress }); result.should.deepEqual(mockResponse); - sinon.assert.calledOnce(mockBitGo.get); - const queryStub = mockBitGo.get.returnValues[0].query; - sinon.assert.calledWith(queryStub, { addresses, destinationAddress }); + sinon.assert.calledOnce(mockBitGo.post); + const sendStub = mockBitGo.post.returnValues[0].send; + sinon.assert.calledWith(sendStub, { addresses, destinationAddress }); }); it('should throw error if addresses is not an array', async function () { @@ -94,11 +94,11 @@ describe('Wallet - getAccountResources', function () { } }); - it('should not include destinationAddress in query if not provided', async function () { + it('should not include destinationAddress in body if not provided', async function () { const mockResponse = { resources: [] }; - mockBitGo.get.returns({ - query: sinon.stub().returns({ + mockBitGo.post.returns({ + send: sinon.stub().returns({ result: sinon.stub().resolves(mockResponse), }), }); @@ -106,10 +106,10 @@ describe('Wallet - getAccountResources', function () { const addresses = ['address1']; await wallet.getAccountResources({ addresses }); - const queryStub = mockBitGo.get.returnValues[0].query; - const queryArg = queryStub.firstCall.args[0]; - queryArg.should.deepEqual({ addresses }); - queryArg.should.not.have.property('destinationAddress'); + const sendStub = mockBitGo.post.returnValues[0].send; + const bodyArg = sendStub.firstCall.args[0]; + bodyArg.should.deepEqual({ addresses }); + bodyArg.should.not.have.property('destinationAddress'); }); }); }); From 784adf4316dd56a730db15fe52cb0c7c00e2373d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 06:27:56 +0000 Subject: [PATCH 2/2] fix(express): convert limit to string and forward nextBatchPrevId in resource delegations handler Agent-Logs-Url: https://github.com/BitGo/BitGoJS/sessions/c1f514f3-1017-413f-90a9-4de5a30f1f0e Co-authored-by: bhavidhingra <17147510+bhavidhingra@users.noreply.github.com> TICKET: CHALO-346 --- modules/express/src/clientRoutes.ts | 9 +- .../typedRoutes/api/v2/accountResources.ts | 4 +- .../clientRoutes/trxResourceDelegation.ts | 180 ++++++++++++++++++ 3 files changed, 188 insertions(+), 5 deletions(-) create mode 100644 modules/express/test/unit/clientRoutes/trxResourceDelegation.ts diff --git a/modules/express/src/clientRoutes.ts b/modules/express/src/clientRoutes.ts index 25e3947832..cd74055800 100755 --- a/modules/express/src/clientRoutes.ts +++ b/modules/express/src/clientRoutes.ts @@ -1016,7 +1016,9 @@ async function handleV2SendMany(req: ExpressApiRouteRequest<'express.v2.wallet.s * handle get account resources * @param req */ -async function handleV2AccountResources(req: ExpressApiRouteRequest<'express.v2.wallet.getaccountresources', 'post'>) { +export async function handleV2AccountResources( + req: ExpressApiRouteRequest<'express.v2.wallet.getaccountresources', 'post'> +) { const bitgo = req.bitgo; const coin = bitgo.coin(req.decoded.coin); const wallet = await coin.wallets().get({ id: req.decoded.id }); @@ -1030,7 +1032,7 @@ async function handleV2AccountResources(req: ExpressApiRouteRequest<'express.v2. * handle get resource delegations * @param req */ -async function handleV2ResourceDelegations( +export async function handleV2ResourceDelegations( req: ExpressApiRouteRequest<'express.v2.wallet.resourcedelegations', 'get'> ) { const bitgo = req.bitgo; @@ -1039,7 +1041,8 @@ async function handleV2ResourceDelegations( const query: Record = {}; if (req.decoded.type) query.type = req.decoded.type; if (req.decoded.resource) query.resource = req.decoded.resource; - if (req.decoded.limit) query.limit = req.decoded.limit; + if (req.decoded.limit !== undefined) query.limit = String(req.decoded.limit); + if (req.decoded.nextBatchPrevId) query.nextBatchPrevId = req.decoded.nextBatchPrevId; return bitgo .get(bitgo.url(`/${coin}/wallet/${walletId}/resourcedelegations`, 2)) .query(query) diff --git a/modules/express/src/typedRoutes/api/v2/accountResources.ts b/modules/express/src/typedRoutes/api/v2/accountResources.ts index 6c3da9a9eb..b5f0d19014 100644 --- a/modules/express/src/typedRoutes/api/v2/accountResources.ts +++ b/modules/express/src/typedRoutes/api/v2/accountResources.ts @@ -34,6 +34,8 @@ export const AccountResourceInfo = t.intersection([ staked_bandwidth_used: t.number, energy_available: t.number, energy_used: t.number, + }), + t.partial({ resourceDeficitForAssetTransfer: t.intersection([ t.type({ bandwidthDeficit: t.number, @@ -44,8 +46,6 @@ export const AccountResourceInfo = t.intersection([ energySunRequired: t.string, }), ]), - }), - t.partial({ maxResourcesDelegatable: t.type({ bandwidthSun: t.string, energySun: t.string, diff --git a/modules/express/test/unit/clientRoutes/trxResourceDelegation.ts b/modules/express/test/unit/clientRoutes/trxResourceDelegation.ts new file mode 100644 index 0000000000..79a776aea8 --- /dev/null +++ b/modules/express/test/unit/clientRoutes/trxResourceDelegation.ts @@ -0,0 +1,180 @@ +import * as sinon from 'sinon'; +import { decodeOrElse } from '@bitgo/sdk-core'; + +import 'should'; +import 'should-sinon'; +import '../../lib/asserts'; + +import * as express from 'express'; + +import { handleV2AccountResources, handleV2ResourceDelegations } from '../../../src/clientRoutes'; +import { AccountResourcesResponse } from '../../../src/typedRoutes/api/v2/accountResources'; +import { ResourceDelegationsResponse } from '../../../src/typedRoutes/api/v2/resourceDelegations'; + +import { BitGo } from 'bitgo'; + +describe('TRX Resource Delegation handlers', () => { + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.verifyAndRestore(); + }); + + describe('handleV2AccountResources', () => { + const mockResources = { + resources: [ + { + address: 'TAddr123', + free_bandwidth_available: 1500, + free_bandwidth_used: 0, + staked_bandwidth_available: 0, + staked_bandwidth_used: 0, + energy_available: 0, + energy_used: 0, + }, + ], + failedAddresses: [], + }; + + function createMocks(result: unknown) { + const getAccountResourcesStub = sandbox.stub().resolves(result); + const walletStub = { getAccountResources: getAccountResourcesStub }; + const coinStub = { + wallets: () => ({ get: () => Promise.resolve(walletStub) }), + }; + const bitgoStub = sinon.createStubInstance(BitGo as any, { coin: coinStub }); + return { bitgoStub, getAccountResourcesStub }; + } + + it('should call getAccountResources with addresses and return codec-valid result', async () => { + const { bitgoStub, getAccountResourcesStub } = createMocks(mockResources); + + const mockRequest = { + bitgo: bitgoStub, + decoded: { + coin: 'ttrx', + id: 'walletId123', + addresses: ['TAddr123'], + }, + }; + + const result = await handleV2AccountResources(mockRequest as express.Request & typeof mockRequest); + decodeOrElse('AccountResourcesResponse', AccountResourcesResponse[200], result, (_) => { + throw new Error('Response did not match expected codec'); + }); + getAccountResourcesStub.should.be.calledOnceWith({ + addresses: ['TAddr123'], + destinationAddress: undefined, + }); + }); + + it('should forward destinationAddress when provided', async () => { + const { bitgoStub, getAccountResourcesStub } = createMocks(mockResources); + + const mockRequest = { + bitgo: bitgoStub, + decoded: { + coin: 'ttrx', + id: 'walletId123', + addresses: ['TAddr123'], + destinationAddress: 'TDest456', + }, + }; + + await handleV2AccountResources(mockRequest as express.Request & typeof mockRequest); + getAccountResourcesStub.should.be.calledOnceWith({ + addresses: ['TAddr123'], + destinationAddress: 'TDest456', + }); + }); + }); + + describe('handleV2ResourceDelegations', () => { + const mockDelegations = { + address: 'TAddr123', + coin: 'ttrx', + delegations: { + outgoing: [], + incoming: [], + }, + }; + + function createBitgoStub(result: unknown) { + const resultStub = sandbox.stub().resolves(result); + const queryStub = sandbox.stub().returns({ result: resultStub }); + const getStub = sandbox.stub().returns({ query: queryStub }); + const urlStub = sandbox + .stub() + .returns('https://test.bitgo.com/api/v2/ttrx/wallet/walletId123/resourcedelegations'); + const bitgoStub = sinon.createStubInstance(BitGo as any, { get: getStub, url: urlStub }); + return { bitgoStub, getStub, queryStub, resultStub }; + } + + it('should forward type and resource query params', async () => { + const { bitgoStub, queryStub } = createBitgoStub(mockDelegations); + + const mockRequest = { + bitgo: bitgoStub, + decoded: { + coin: 'ttrx', + id: 'walletId123', + type: 'outgoing' as const, + resource: 'ENERGY', + }, + }; + + await handleV2ResourceDelegations(mockRequest as express.Request & typeof mockRequest); + queryStub.should.be.calledOnceWith({ type: 'outgoing', resource: 'ENERGY' }); + }); + + it('should convert limit to string when forwarding', async () => { + const { bitgoStub, queryStub } = createBitgoStub(mockDelegations); + + const mockRequest = { + bitgo: bitgoStub, + decoded: { + coin: 'ttrx', + id: 'walletId123', + limit: 10, + }, + }; + + await handleV2ResourceDelegations(mockRequest as express.Request & typeof mockRequest); + queryStub.should.be.calledOnceWith({ limit: '10' }); + }); + + it('should forward nextBatchPrevId for pagination', async () => { + const { bitgoStub, queryStub } = createBitgoStub(mockDelegations); + + const mockRequest = { + bitgo: bitgoStub, + decoded: { + coin: 'ttrx', + id: 'walletId123', + type: 'incoming' as const, + nextBatchPrevId: 'cursor-abc123', + }, + }; + + await handleV2ResourceDelegations(mockRequest as express.Request & typeof mockRequest); + queryStub.should.be.calledOnceWith({ type: 'incoming', nextBatchPrevId: 'cursor-abc123' }); + }); + + it('should return codec-valid delegations result', async () => { + const { bitgoStub } = createBitgoStub(mockDelegations); + + const mockRequest = { + bitgo: bitgoStub, + decoded: { + coin: 'ttrx', + id: 'walletId123', + }, + }; + + const result = await handleV2ResourceDelegations(mockRequest as express.Request & typeof mockRequest); + decodeOrElse('ResourceDelegationsResponse', ResourceDelegationsResponse[200], result, (_) => { + throw new Error('Response did not match expected codec'); + }); + }); + }); +});