From ecca18960fd123f087596dc2a1930bc5fe142bef Mon Sep 17 00:00:00 2001 From: John Whiles Date: Wed, 30 Sep 2026 09:36:59 +0100 Subject: [PATCH] feat: allow chomp api service to handle unknown intents in response --- packages/chomp-api-service/CHANGELOG.md | 6 ++ .../src/chomp-api-service.test.ts | 63 +++++++++++++++++++ .../src/chomp-api-service.ts | 20 ++++-- 3 files changed, 85 insertions(+), 4 deletions(-) diff --git a/packages/chomp-api-service/CHANGELOG.md b/packages/chomp-api-service/CHANGELOG.md index 9bcb2d1a140..8dc8f8f8774 100644 --- a/packages/chomp-api-service/CHANGELOG.md +++ b/packages/chomp-api-service/CHANGELOG.md @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump `@tanstack/query-core` from `^5.89.0` to `^5.103.2` ([#10511](https://github.com/MetaMask/core/pull/10511)) +### Fixed + +- Ignore unrecognised intent types returned by the CHOMP API instead of failing to parse the whole response ([#10609](https://github.com/MetaMask/core/pull/10609)) + - `getIntentsByAddress` now omits intents whose `metadata.type` is not a known `ChompIntentType`. + - `getServiceDetails` now omits unknown values from each protocol's `intentTypes`. + ## [6.0.0] ### Added diff --git a/packages/chomp-api-service/src/chomp-api-service.test.ts b/packages/chomp-api-service/src/chomp-api-service.test.ts index dbf7575adda..601778f7723 100644 --- a/packages/chomp-api-service/src/chomp-api-service.test.ts +++ b/packages/chomp-api-service/src/chomp-api-service.test.ts @@ -634,6 +634,38 @@ describe('ChompApiService', () => { }); } + it('omits intents with an unknown intent type', async () => { + const unknownIntent = { + ...intentsResponse[0], + delegationHash: '0x456', + metadata: { ...intentsResponse[0].metadata, type: 'some-future-type' }, + }; + nock(BASE_URL) + .get('/v1/intent/account/0xabc') + .reply(200, [unknownIntent, ...intentsResponse]); + const { service } = createService(); + + expect(await service.getIntentsByAddress('0xabc')).toStrictEqual( + intentsResponse, + ); + }); + + it('throws when an intent type is not a string', async () => { + nock(BASE_URL) + .get('/v1/intent/account/0xabc') + .reply(200, [ + { + ...intentsResponse[0], + metadata: { ...intentsResponse[0].metadata, type: 123 }, + }, + ]); + const { service } = createService(); + + await expect(service.getIntentsByAddress('0xabc')).rejects.toThrow( + 'At path: 0.metadata.type -- Expected a string, but received: 123', + ); + }); + it('throws on malformed response', async () => { nock(BASE_URL) .get('/v1/intent/account/0xabc') @@ -769,6 +801,37 @@ describe('ChompApiService', () => { ); }); + it('omits unknown intent types from a protocol', async () => { + const response = { + ...serviceDetailsResponse, + chains: { + '0xa4b1': { + ...serviceDetailsResponse.chains['0xa4b1'], + protocol: { + vedaProtocol: { + ...serviceDetailsResponse.chains['0xa4b1'].protocol + .vedaProtocol, + intentTypes: [ + 'cash-deposit', + 'some-future-type', + 'cash-withdrawal', + ], + }, + }, + }, + }, + }; + nock(BASE_URL) + .get('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/v1/chomp') + .query({ chainId: '0xa4b1' }) + .reply(200, response); + const { service } = createService(); + + expect(await service.getServiceDetails(['0xa4b1'])).toStrictEqual( + serviceDetailsResponse, + ); + }); + it('supports multiple chain IDs as a comma-separated query param', async () => { nock(BASE_URL) .get('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/v1/chomp') diff --git a/packages/chomp-api-service/src/chomp-api-service.ts b/packages/chomp-api-service/src/chomp-api-service.ts index a57a9a0b230..321d55e2fff 100644 --- a/packages/chomp-api-service/src/chomp-api-service.ts +++ b/packages/chomp-api-service/src/chomp-api-service.ts @@ -56,6 +56,10 @@ const CHOMP_INTENT_TYPES: ChompIntentType[] = [ 'cash-subscription', ]; +function isChompIntentType(value: string): value is ChompIntentType { + return (CHOMP_INTENT_TYPES as string[]).includes(value); +} + // === GENERAL === /** @@ -225,7 +229,7 @@ const IntentEntryArrayStruct = array( allowance: StrictHexStruct, tokenAddress: StrictHexStruct, tokenSymbol: string(), - type: enums(CHOMP_INTENT_TYPES), + type: string(), }), }), ); @@ -242,7 +246,11 @@ const ServiceDetailsProtocolStruct = type({ }), ), adapterAddress: StrictHexStruct, - intentTypes: array(enums(CHOMP_INTENT_TYPES)), + intentTypes: coerce( + array(enums(CHOMP_INTENT_TYPES)), + array(string()), + (intentTypes) => intentTypes.filter(isChompIntentType), + ), }); const ServiceDetailsResponseStruct = type({ @@ -612,7 +620,8 @@ export class ChompApiService extends BaseDataService< * is always fetched fresh (`staleTime: 0`). * * @param address - The address to look up intents for. - * @returns The array of intents for the address. + * @returns The array of intents for the address, omitting any with an + * intent type this service does not recognise. */ async getIntentsByAddress(address: Hex): Promise { const jsonResponse = await this.fetchQuery({ @@ -636,7 +645,10 @@ export class ChompApiService extends BaseDataService< }, }); - return create(jsonResponse, IntentEntryArrayStruct); + return create(jsonResponse, IntentEntryArrayStruct).filter( + (intent): intent is IntentEntry => + isChompIntentType(intent.metadata.type), + ); } /**