From 85463efef4069ffe031033033e3ec6941aa3a77e Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Thu, 27 Aug 2026 15:46:00 -0600 Subject: [PATCH 1/4] fix(deps): bump @shapeshiftoss packages and read swapperMetadata Relay swaps registered since the normalized-metadata change were stuck PENDING forever. Clients write `metadata.swapperMetadata`, but swapper was pinned at 17.8.0, which reads the flat `relayTransactionMetadata` and throws `Error: Missing swap metadata` on every poll. The data was correct; the SDK was three majors stale. swapper 17.8.0 -> 20.0.0, types 8.6.8 -> 9.0.0, chain-adapters 11.4.0 -> 11.5.1, caip 8.16.9 -> 8.16.10. unchained-client and unchained-pulumi were already current. 20.0.0 declares exactly the shape being written: `swapperMetadata?: SwapperMetadata`, a union discriminated on `name`. Follow-on changes: - `SwapperSpecificMetadata` is gone, replaced by `SwapMetadata` - `SwapperConfig` dropped the three VITE_TENDERLY_* keys and now requires VITE_RELAY_API_KEY and VITE_ACROSS_API_KEY - the three verifier reads move to the SDK's own `getSwapMetadata` accessor. The chainflip one was previously behind a `Record` cast, so it was silently broken rather than failing to compile - test fixtures rewritten to the new metadata shape No fallback for the 19 rows still holding the legacy keys: all are terminal, and none match getPendingVerificationSwaps, so nothing re-reads them. Co-Authored-By: Claude Opus 5 (1M context) --- apps/swap-service/src/swaps/swapper-config.ts | 5 +- apps/swap-service/src/swaps/types.ts | 4 +- apps/swap-service/src/swaps/utils.ts | 4 +- .../__tests__/fixtures/mayachain/swap.ts | 11 +- .../__tests__/fixtures/near/swap.ts | 12 +- .../__tests__/fixtures/relay/swap.ts | 17 +-- .../__tests__/fixtures/thorchain/swap.ts | 11 +- .../src/verification/__tests__/near.test.ts | 8 +- .../src/verification/__tests__/relay.test.ts | 8 +- .../src/verification/__tests__/setup.ts | 2 + .../verification/swap-verification.service.ts | 14 +-- package.json | 8 +- yarn.lock | 113 ++++++++++++++---- 13 files changed, 128 insertions(+), 89 deletions(-) diff --git a/apps/swap-service/src/swaps/swapper-config.ts b/apps/swap-service/src/swaps/swapper-config.ts index eddc446..f55db51 100644 --- a/apps/swap-service/src/swaps/swapper-config.ts +++ b/apps/swap-service/src/swaps/swapper-config.ts @@ -13,6 +13,7 @@ import type { TronChainAdapterService } from '../lib/chain-adapters/tron.service import type { UtxoChainAdapterService } from '../lib/chain-adapters/utxo.service' export const getSwapperConfig = (): SwapperConfig => ({ + VITE_ACROSS_API_KEY: '', VITE_ACROSS_API_URL: env.VITE_ACROSS_API_URL, VITE_ACROSS_INTEGRATOR_ID: '', VITE_BEBOP_API_KEY: env.VITE_BEBOP_API_KEY, @@ -28,11 +29,9 @@ export const getSwapperConfig = (): SwapperConfig => ({ VITE_MAYACHAIN_NODE_URL: env.VITE_MAYACHAIN_NODE_URL, VITE_NEAR_INTENTS_API_KEY: env.VITE_NEAR_INTENTS_API_KEY, VITE_PORTALS_BASE_URL: env.VITE_PORTALS_BASE_URL, + VITE_RELAY_API_KEY: '', VITE_RELAY_API_URL: env.VITE_RELAY_API_URL, VITE_SUI_NODE_URL: env.VITE_SUI_NODE_URL, - VITE_TENDERLY_ACCOUNT_SLUG: '', - VITE_TENDERLY_API_KEY: '', - VITE_TENDERLY_PROJECT_SLUG: '', VITE_THORCHAIN_MIDGARD_URL: env.VITE_THORCHAIN_MIDGARD_URL, VITE_THORCHAIN_NODE_URL: env.VITE_THORCHAIN_NODE_URL, VITE_TRON_GRID_API_KEY: '', diff --git a/apps/swap-service/src/swaps/types.ts b/apps/swap-service/src/swaps/types.ts index 66fdd47..b0e5200 100644 --- a/apps/swap-service/src/swaps/types.ts +++ b/apps/swap-service/src/swaps/types.ts @@ -2,7 +2,7 @@ import type { Prisma, Swap as PrismaSwap } from '@prisma/client' import { Type } from 'class-transformer' import { IsInt, IsOptional, IsString, Max, Min } from 'class-validator' -import type { SwapperName, SwapperSpecificMetadata } from '@shapeshiftoss/swapper' +import type { SwapMetadata, SwapperName } from '@shapeshiftoss/swapper' import type { Asset } from '@shapeshiftoss/types' export type Swap = Omit< @@ -12,7 +12,7 @@ export type Swap = Omit< sellAsset: Asset buyAsset: Asset swapperName: SwapperName - metadata: SwapperSpecificMetadata + metadata: SwapMetadata affiliateVerificationDetails: AffiliateVerificationDetails | null } diff --git a/apps/swap-service/src/swaps/utils.ts b/apps/swap-service/src/swaps/utils.ts index 77be2d0..8f00e04 100644 --- a/apps/swap-service/src/swaps/utils.ts +++ b/apps/swap-service/src/swaps/utils.ts @@ -5,7 +5,7 @@ import type { CreateSwapDto } from '@shapeshift/shared-types' import { baseUnitToPrecision } from '@shapeshift/shared-utils' import { mayachainAssetId, thorchainAssetId } from '@shapeshiftoss/caip' import { bnOrZero } from '@shapeshiftoss/chain-adapters' -import type { Swap as SwapperSwap, SwapperName, SwapperSpecificMetadata } from '@shapeshiftoss/swapper' +import type { Swap as SwapperSwap, SwapMetadata, SwapperName } from '@shapeshiftoss/swapper' import type { Asset } from '@shapeshiftoss/types' import { getAssetPriceUsd } from '../utils/pricing' @@ -37,7 +37,7 @@ export const toSwap = (swap: PrismaSwap): Swap => ({ swapperName: swap.swapperName as SwapperName, sellAsset: swap.sellAsset as Asset, buyAsset: swap.buyAsset as Asset, - metadata: swap.metadata as SwapperSpecificMetadata, + metadata: swap.metadata as SwapMetadata, affiliateVerificationDetails: toAffiliateVerificationDetails(swap.affiliateVerificationDetails), }) diff --git a/apps/swap-service/src/verification/__tests__/fixtures/mayachain/swap.ts b/apps/swap-service/src/verification/__tests__/fixtures/mayachain/swap.ts index 7e2f4ee..c445c1b 100644 --- a/apps/swap-service/src/verification/__tests__/fixtures/mayachain/swap.ts +++ b/apps/swap-service/src/verification/__tests__/fixtures/mayachain/swap.ts @@ -57,13 +57,10 @@ export default { metadata: { quoteId: '562f173c-1515-4a4e-bf6b-cd1b35a9db1f', stepIndex: 0, - acrossTransactionMetadata: undefined, - chainflipSwapId: undefined, - debridgeTransactionMetadata: undefined, - relayerExplorerTxLink: undefined, - relayerTxHash: undefined, - relayTransactionMetadata: undefined, - streamingSwapMetadata: undefined, + swapperMetadata: { + name: 'mayachain', + maxStreamingQuantity: undefined, + }, }, userId: 'api', referralCode: null, diff --git a/apps/swap-service/src/verification/__tests__/fixtures/near/swap.ts b/apps/swap-service/src/verification/__tests__/fixtures/near/swap.ts index b4a1928..63a593d 100644 --- a/apps/swap-service/src/verification/__tests__/fixtures/near/swap.ts +++ b/apps/swap-service/src/verification/__tests__/fixtures/near/swap.ts @@ -51,18 +51,10 @@ export default { metadata: { quoteId: '6785cf2e-683b-4335-9050-eab792045af6', stepIndex: 0, - nearIntentsSpecific: { - deadline: '2026-04-28T17:52:55.658Z', - timeEstimate: 52, + swapperMetadata: { + name: 'nearIntents', depositAddress: '0xE08B3621624e6CA17B8F19a854fc5D6f14CAa120', }, - acrossTransactionMetadata: undefined, - chainflipSwapId: undefined, - debridgeTransactionMetadata: undefined, - relayerExplorerTxLink: undefined, - relayerTxHash: undefined, - relayTransactionMetadata: undefined, - streamingSwapMetadata: undefined, }, userId: 'api', referralCode: null, diff --git a/apps/swap-service/src/verification/__tests__/fixtures/relay/swap.ts b/apps/swap-service/src/verification/__tests__/fixtures/relay/swap.ts index 3dae425..278ee67 100644 --- a/apps/swap-service/src/verification/__tests__/fixtures/relay/swap.ts +++ b/apps/swap-service/src/verification/__tests__/fixtures/relay/swap.ts @@ -51,22 +51,11 @@ export default { metadata: { quoteId: '5d674878-548f-4f36-8b50-dcb41ef1b0c0', stepIndex: 0, - relayTransactionMetadata: { - to: '0xb92fe925dc43a0ecde6c8b1a2709c170ec4fff4f', - data: '0xcd6e13f70000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000a60000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c4a6bd8c9600000000000000000000000000000000000000000000000000000600aea7d000000000000000000000000000f70da97812cb96acdf810712aa562db8dfa3dbef000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000215f30ab2f9dc7b36f0c2af18173c477fb716e7a8bb74912435b01ae52307536ee0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ff3684f28c67538d4d072c2273400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003877df61eb000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000007c42213bc0b0000000000000000000000007f54f05635d15cde17a49502fedb9d1803a3be8a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003877df61eb0000000000000000000000000007f54f05635d15cde17a49502fedb9d1803a3be8a00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000006e41fff991f000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000022552800000000000000000000000000000000000000000000000000000000000000a03d98144ca848477bf71835710af43c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000005600000000000000000000000000000000000000000000000000000000000000044bd01c2260000000000000000000000000000000000000000000000000000000069f0ff0f0000000000000000000000000000000000000000000000000003877df61eb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010438c9c147000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000002710000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000024d0e30db000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010438c9c147000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000002710000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000184f61460f90000000000000000000000007f54f05635d15cde17a49502fedb9d1803a3be8a000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000ffffffffffffffc5000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043271000000000400065a8177fae2701a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48517e506700271aea091b02f42756f5e174af5230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008434ee90ca000000000000000000000000f5c4f3dc02c3fb9279495a8fef7b0741da956157000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000022ad960000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a49bb43718000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a44c286ba83bb771cd0107b2c1df678435bd15350000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000215f30ab2f9dc7b36f0c2af18173c477fb716e7a8bb74912435b01ae52307536ee00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000215f30ab2f9dc7b36f0c2af18173c477fb716e7a8bb74912435b01ae52307536ee0000000000000000000000000000000000000000000000000000000000000000ee63570325ea10b53421947bb8a7e617bf774c37181fa2c0f63b7cd9f2ba03f5', - from: '0xA44C286BA83Bb771cd0107B2c1Df678435Bd1535', - value: '1000000000000000', + swapperMetadata: { + name: 'relay', relayId: '0xee63570325ea10b53421947bb8a7e617bf774c37181fa2c0f63b7cd9f2ba03f5', - orderId: undefined, - gasLimit: '382740', + data: '0xcd6e13f70000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000a60000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c4a6bd8c9600000000000000000000000000000000000000000000000000000600aea7d000000000000000000000000000f70da97812cb96acdf810712aa562db8dfa3dbef000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000215f30ab2f9dc7b36f0c2af18173c477fb716e7a8bb74912435b01ae52307536ee0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ff3684f28c67538d4d072c2273400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003877df61eb000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000007c42213bc0b0000000000000000000000007f54f05635d15cde17a49502fedb9d1803a3be8a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003877df61eb0000000000000000000000000007f54f05635d15cde17a49502fedb9d1803a3be8a00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000006e41fff991f000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000022552800000000000000000000000000000000000000000000000000000000000000a03d98144ca848477bf71835710af43c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000005600000000000000000000000000000000000000000000000000000000000000044bd01c2260000000000000000000000000000000000000000000000000000000069f0ff0f0000000000000000000000000000000000000000000000000003877df61eb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010438c9c147000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000002710000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000024d0e30db000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010438c9c147000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000002710000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000242e1a7d4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000184f61460f90000000000000000000000007f54f05635d15cde17a49502fedb9d1803a3be8a000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000ffffffffffffffc5000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043271000000000400065a8177fae2701a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48517e506700271aea091b02f42756f5e174af5230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008434ee90ca000000000000000000000000f5c4f3dc02c3fb9279495a8fef7b0741da956157000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000022ad960000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a49bb43718000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a44c286ba83bb771cd0107b2c1df678435bd15350000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000215f30ab2f9dc7b36f0c2af18173c477fb716e7a8bb74912435b01ae52307536ee00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000215f30ab2f9dc7b36f0c2af18173c477fb716e7a8bb74912435b01ae52307536ee0000000000000000000000000000000000000000000000000000000000000000ee63570325ea10b53421947bb8a7e617bf774c37181fa2c0f63b7cd9f2ba03f5', }, - acrossTransactionMetadata: undefined, - chainflipSwapId: undefined, - debridgeTransactionMetadata: undefined, - relayerExplorerTxLink: undefined, - relayerTxHash: undefined, - streamingSwapMetadata: undefined, - nearIntentsSpecific: undefined, }, userId: 'api', referralCode: null, diff --git a/apps/swap-service/src/verification/__tests__/fixtures/thorchain/swap.ts b/apps/swap-service/src/verification/__tests__/fixtures/thorchain/swap.ts index d258f5f..47cc9f4 100644 --- a/apps/swap-service/src/verification/__tests__/fixtures/thorchain/swap.ts +++ b/apps/swap-service/src/verification/__tests__/fixtures/thorchain/swap.ts @@ -57,13 +57,10 @@ export default { metadata: { quoteId: 'ca88e6ef-ca09-4848-98a6-1bd18e36fc81', stepIndex: 0, - acrossTransactionMetadata: undefined, - chainflipSwapId: undefined, - debridgeTransactionMetadata: undefined, - relayerExplorerTxLink: undefined, - relayerTxHash: undefined, - relayTransactionMetadata: undefined, - streamingSwapMetadata: undefined, + swapperMetadata: { + name: 'thorchain', + maxStreamingQuantity: undefined, + }, }, userId: 'api', referralCode: null, diff --git a/apps/swap-service/src/verification/__tests__/near.test.ts b/apps/swap-service/src/verification/__tests__/near.test.ts index 40ae2ce..fb297de 100644 --- a/apps/swap-service/src/verification/__tests__/near.test.ts +++ b/apps/swap-service/src/verification/__tests__/near.test.ts @@ -1,7 +1,7 @@ import { OneClickService } from '@defuse-protocol/one-click-sdk-typescript' import type { HttpService } from '@nestjs/axios' -import type { SwapperSpecificMetadata } from '@shapeshiftoss/swapper' +import type { SwapMetadata } from '@shapeshiftoss/swapper' import type { Swap } from '../../swaps/types' import { SwapVerificationService } from '../swap-verification.service' @@ -104,15 +104,15 @@ describe('verifyNearIntents', () => { expect(result.verifiedSellAmountCryptoBaseUnit).toBe(response.quoteResponse.quote.amountIn) }) - it('returns FAILED when nearIntentsSpecific.depositAddress is missing', async () => { - const swapWithoutMetadata = { ...swap, metadata: {} as SwapperSpecificMetadata } as Swap + it('returns FAILED when nearIntents metadata is missing', async () => { + const swapWithoutMetadata = { ...swap, metadata: {} as SwapMetadata } as Swap const result = await service.verifySwap(swapWithoutMetadata) expect(result).toMatchObject({ verificationStatus: 'FAILED', hasAffiliate: false, - noAffiliateReason: 'Missing depositAddress in nearIntentsSpecific metadata', + noAffiliateReason: 'Missing depositAddress in nearIntents metadata', }) }) diff --git a/apps/swap-service/src/verification/__tests__/relay.test.ts b/apps/swap-service/src/verification/__tests__/relay.test.ts index 97e5f60..1705d90 100644 --- a/apps/swap-service/src/verification/__tests__/relay.test.ts +++ b/apps/swap-service/src/verification/__tests__/relay.test.ts @@ -1,7 +1,7 @@ import type { HttpService } from '@nestjs/axios' import { of, throwError } from 'rxjs' -import type { SwapperSpecificMetadata } from '@shapeshiftoss/swapper' +import type { SwapMetadata } from '@shapeshiftoss/swapper' import type { Swap } from '../../swaps/types' import { SwapVerificationService } from '../swap-verification.service' @@ -183,8 +183,8 @@ describe('verifyRelay', () => { expect(result.actualAffiliateFeeAmountCryptoBaseUnit).toBe('999') }) - it('returns FAILED when relayTransactionMetadata.relayId is missing', async () => { - const swapWithoutMetadata = { ...swap, metadata: {} as SwapperSpecificMetadata } as Swap + it('returns FAILED when relay metadata is missing', async () => { + const swapWithoutMetadata = { ...swap, metadata: {} as SwapMetadata } as Swap service = new SwapVerificationService(makeHttpMock(relayResponse)) @@ -193,7 +193,7 @@ describe('verifyRelay', () => { expect(result).toMatchObject({ verificationStatus: 'FAILED', hasAffiliate: false, - noAffiliateReason: 'Missing relayId in relayTransactionMetadata', + noAffiliateReason: 'Missing relayId in relay metadata', }) }) diff --git a/apps/swap-service/src/verification/__tests__/setup.ts b/apps/swap-service/src/verification/__tests__/setup.ts index cd35b37..271ccdd 100644 --- a/apps/swap-service/src/verification/__tests__/setup.ts +++ b/apps/swap-service/src/verification/__tests__/setup.ts @@ -59,6 +59,8 @@ jest.mock('@shapeshiftoss/swapper', () => ({ Debridge: 'deBridge', }, assertGetCowNetwork: jest.fn(), + getSwapMetadata: (metadata: { name?: string } | undefined, name: string) => + metadata?.name === name ? metadata : undefined, getTreasuryAddressFromChainId: jest.fn(), relayTokenToAssetId: (token: { chainId: number; address: string }) => { const isNative = token.address.toLowerCase() === '0x0000000000000000000000000000000000000000' diff --git a/apps/swap-service/src/verification/swap-verification.service.ts b/apps/swap-service/src/verification/swap-verification.service.ts index f57ae52..792f89d 100644 --- a/apps/swap-service/src/verification/swap-verification.service.ts +++ b/apps/swap-service/src/verification/swap-verification.service.ts @@ -6,6 +6,7 @@ import { firstValueFrom } from 'rxjs' import { SwapVerificationResult } from '@shapeshift/shared-types' import { assertGetCowNetwork, + getSwapMetadata, getTreasuryAddressFromChainId, relayTokenToAssetId, SwapperName, @@ -127,8 +128,8 @@ export class SwapVerificationService { private async verifyNearIntents(swap: Swap): Promise { const { metadata } = swap - const depositAddress = metadata.nearIntentsSpecific?.depositAddress - if (!depositAddress) return noAffiliateResult('FAILED', 'Missing depositAddress in nearIntentsSpecific metadata') + const depositAddress = getSwapMetadata(metadata.swapperMetadata, 'nearIntents')?.depositAddress + if (!depositAddress) return noAffiliateResult('FAILED', 'Missing depositAddress in nearIntents metadata') const status = await OneClickService.getExecutionStatus(depositAddress) @@ -160,8 +161,8 @@ export class SwapVerificationService { private async verifyRelay(swap: Swap): Promise { const { metadata } = swap - const relayId = metadata.relayTransactionMetadata?.relayId - if (!relayId) return noAffiliateResult('FAILED', 'Missing relayId in relayTransactionMetadata') + const relayId = getSwapMetadata(metadata.swapperMetadata, 'relay')?.relayId + if (!relayId) return noAffiliateResult('FAILED', 'Missing relayId in relay metadata') const { data } = await firstValueFrom( this.httpService.get(`${env.VITE_RELAY_API_URL}/requests/v2?id=${relayId}`), @@ -391,10 +392,9 @@ export class SwapVerificationService { } private async verifyChainflip(swap: Swap): Promise { - const metadata = swap.metadata as Record - const chainflipSwapId = metadata?.chainflipSwapId as string | undefined + const chainflipSwapId = getSwapMetadata(swap.metadata.swapperMetadata, 'chainflip')?.swapId - if (!chainflipSwapId) return noAffiliateResult('FAILED', 'Missing chainflipSwapId in metadata') + if (!chainflipSwapId) return noAffiliateResult('FAILED', 'Missing swapId in chainflip metadata') const statusUrl = `${this.chainflipApiUrl}/swaps/${chainflipSwapId}` diff --git a/package.json b/package.json index f6fdb8a..698401b 100644 --- a/package.json +++ b/package.json @@ -51,10 +51,10 @@ "@prisma/client": "6.13.0", "@pulumi/kubernetes": "4.23.0", "@pulumi/pulumi": "3.160.0", - "@shapeshiftoss/caip": "8.16.9", - "@shapeshiftoss/chain-adapters": "11.4.0", - "@shapeshiftoss/swapper": "17.8.0", - "@shapeshiftoss/types": "8.6.8", + "@shapeshiftoss/caip": "8.16.10", + "@shapeshiftoss/chain-adapters": "11.5.1", + "@shapeshiftoss/swapper": "20.0.0", + "@shapeshiftoss/types": "9.0.0", "@shapeshiftoss/unchained-client": "10.14.11", "@shapeshiftoss/unchained-pulumi": "1.0.2", "axios": "^1.7.4", diff --git a/yarn.lock b/yarn.lock index 70e0cf8..cac11dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2431,9 +2431,9 @@ __metadata: languageName: node linkType: hard -"@gobob/bob-sdk@npm:5.6.0": - version: 5.6.0 - resolution: "@gobob/bob-sdk@npm:5.6.0" +"@gobob/bob-sdk@npm:5.8.1": + version: 5.8.1 + resolution: "@gobob/bob-sdk@npm:5.8.1" dependencies: "@bitcoinerlab/secp256k1": "npm:^1.2.0" "@eslint/eslintrc": "npm:^3.3.5" @@ -2448,7 +2448,7 @@ __metadata: global: "npm:^4.4.0" globals: "npm:^17.4.0" viem: "npm:^2.33.2" - checksum: 10/dd285ad42b6579bcfd1c42d9bacff4878a84f212db6d4ddcff42c26a004fe60b72d1b6c51a661b95ec97ae2208e01f222f2338d7a241a3b1491d4138948d52d3 + checksum: 10/fbc578125223d11695957981627a73efad876e7785cd6f6156fecec1e9e9c7f986bd0e2cf22fab8764397608c43b562059270d964b72d492479e2961e275fdc8 languageName: node linkType: hard @@ -5538,7 +5538,16 @@ __metadata: languageName: node linkType: hard -"@shapeshiftoss/caip@npm:8.16.9, @shapeshiftoss/caip@npm:^8.16.9": +"@shapeshiftoss/caip@npm:8.16.10, @shapeshiftoss/caip@npm:^8.16.10": + version: 8.16.10 + resolution: "@shapeshiftoss/caip@npm:8.16.10" + dependencies: + axios: "npm:^1.13.5" + checksum: 10/ad0e91258519fabf96b0b4c36f171156a8031d5b35d3508ebfbb26665ab00cecb85b0bc2e889f59bcc0c96643c57c1460607ea6b9c991499535533b442bafc99 + languageName: node + linkType: hard + +"@shapeshiftoss/caip@npm:^8.16.9": version: 8.16.9 resolution: "@shapeshiftoss/caip@npm:8.16.9" dependencies: @@ -5547,19 +5556,19 @@ __metadata: languageName: node linkType: hard -"@shapeshiftoss/chain-adapters@npm:11.4.0, @shapeshiftoss/chain-adapters@npm:^11.4.0": - version: 11.4.0 - resolution: "@shapeshiftoss/chain-adapters@npm:11.4.0" +"@shapeshiftoss/chain-adapters@npm:11.5.1, @shapeshiftoss/chain-adapters@npm:^11.5.1": + version: 11.5.1 + resolution: "@shapeshiftoss/chain-adapters@npm:11.5.1" dependencies: "@mysten/sui": "npm:1.45.0" "@near-js/crypto": "npm:^2.5.1" "@near-js/providers": "npm:^2.5.1" "@near-js/transactions": "npm:^2.5.1" "@near-js/utils": "npm:^2.5.1" - "@shapeshiftoss/caip": "npm:^8.16.9" + "@shapeshiftoss/caip": "npm:^8.16.10" "@shapeshiftoss/hdwallet-core": "npm:^1.62.41" "@shapeshiftoss/hdwallet-ledger": "npm:^1.62.41" - "@shapeshiftoss/types": "npm:^8.6.8" + "@shapeshiftoss/types": "npm:^9.0.0" "@shapeshiftoss/unchained-client": "npm:^10.14.11" "@shapeshiftoss/utils": "npm:^1.1.0" "@solana/spl-token": "npm:^0.4.9" @@ -5576,8 +5585,8 @@ __metadata: multicoin-address-validator: "npm:^0.5.12" node-polyglot: "npm:^2.4.0" p-queue: "npm:^8.0.1" - viem: "npm:2.43.5" - checksum: 10/d88e6a69106f19200b5acbea0f6e403a502be653bedd9d740580510c67167f00744e33029ef144d361b4aa4447b6887640687e58cbf3726546b510a8d0ca5f11 + viem: "npm:2.55.11" + checksum: 10/9a5f0e60da0238f90504dc5fb5876c50da91192b306c9b35d97f2d7f1e35150d57600fc8c5aae475d007b38f634e0a6525e27f86fc6576498cef9dc95d06b8ff languageName: node linkType: hard @@ -5689,9 +5698,9 @@ __metadata: languageName: node linkType: hard -"@shapeshiftoss/swapper@npm:17.8.0": - version: 17.8.0 - resolution: "@shapeshiftoss/swapper@npm:17.8.0" +"@shapeshiftoss/swapper@npm:20.0.0": + version: 20.0.0 + resolution: "@shapeshiftoss/swapper@npm:20.0.0" dependencies: "@arbitrum/sdk": "npm:^4.0.1" "@avnu/avnu-sdk": "npm:^4.0.1" @@ -5700,14 +5709,14 @@ __metadata: "@coral-xyz/anchor": "npm:0.29.0" "@cowprotocol/app-data": "npm:^2.3.0" "@defuse-protocol/one-click-sdk-typescript": "npm:^0.1.1-0.2" - "@gobob/bob-sdk": "npm:5.6.0" + "@gobob/bob-sdk": "npm:5.8.1" "@mysten/sui": "npm:^1.45.2" "@shapeshiftoss/bitcoinjs-lib": "npm:7.0.0-shapeshift.0" - "@shapeshiftoss/caip": "npm:^8.16.9" - "@shapeshiftoss/chain-adapters": "npm:^11.4.0" + "@shapeshiftoss/caip": "npm:^8.16.10" + "@shapeshiftoss/chain-adapters": "npm:^11.5.1" "@shapeshiftoss/contracts": "npm:^1.0.7" "@shapeshiftoss/hdwallet-core": "npm:^1.62.41" - "@shapeshiftoss/types": "npm:^8.6.8" + "@shapeshiftoss/types": "npm:^9.0.0" "@shapeshiftoss/unchained-client": "npm:^10.14.11" "@shapeshiftoss/utils": "npm:^1.1.0" "@sniptt/monads": "npm:^0.5.10" @@ -5728,12 +5737,24 @@ __metadata: qs: "npm:^6.14.2" retry-axios: "npm:^2.6.0" uuid: "npm:^9.0.0" + viem: "npm:2.55.11" + checksum: 10/567b2be0e19ee1fb32a8f74fa42393c468af31c54551e3fffeb0254185144c4c156e098cfbe3b34ee0d36ec56e01c13a3f1759bd50ecab59cd8dcc29061d2197 + languageName: node + linkType: hard + +"@shapeshiftoss/types@npm:9.0.0, @shapeshiftoss/types@npm:^9.0.0": + version: 9.0.0 + resolution: "@shapeshiftoss/types@npm:9.0.0" + dependencies: + "@cowprotocol/app-data": "npm:^2.3.0" + "@shapeshiftoss/caip": "npm:^8.16.10" + ethers5: "npm:ethers@5.7.2" viem: "npm:2.43.5" - checksum: 10/f631917c1f2e35557d9283a02d42db3fbe7cddf47b6f0b251a03a1c136aa07d2729612d7e1a707405994ef1a3c5dbcc32e119d2e4ddba5db1a5d0b9d78aed4ee + checksum: 10/93dd436bf49d1f75e1748dace687244792e8991ef059a6735fbbd46b905ac71e6e83cee6e8f35939154a7125a56dc1f1bddadb0e1028ab623a6c187308383876 languageName: node linkType: hard -"@shapeshiftoss/types@npm:8.6.8, @shapeshiftoss/types@npm:^8.6.8": +"@shapeshiftoss/types@npm:^8.6.8": version: 8.6.8 resolution: "@shapeshiftoss/types@npm:8.6.8" dependencies: @@ -15925,6 +15946,27 @@ __metadata: languageName: node linkType: hard +"ox@npm:0.14.33": + version: 0.14.33 + resolution: "ox@npm:0.14.33" + dependencies: + "@adraffy/ens-normalize": "npm:^1.11.0" + "@noble/ciphers": "npm:^1.3.0" + "@noble/curves": "npm:1.9.1" + "@noble/hashes": "npm:^1.8.0" + "@scure/bip32": "npm:^1.7.0" + "@scure/bip39": "npm:^1.6.0" + abitype: "npm:^1.2.3" + eventemitter3: "npm:5.0.1" + peerDependencies: + typescript: ">=5.4.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/8a1390fa4799fc8629ea64c00b49fe4c5c93417c92033bad7599a1861842af202303ea1cdc85023f19145192563ebe9085609d84b0a3d0145ed1b61e579a2052 + languageName: node + linkType: hard + "ox@npm:^0.4.4": version: 0.4.4 resolution: "ox@npm:0.4.4" @@ -17538,10 +17580,10 @@ __metadata: "@prisma/client": "npm:6.13.0" "@pulumi/kubernetes": "npm:4.23.0" "@pulumi/pulumi": "npm:3.160.0" - "@shapeshiftoss/caip": "npm:8.16.9" - "@shapeshiftoss/chain-adapters": "npm:11.4.0" - "@shapeshiftoss/swapper": "npm:17.8.0" - "@shapeshiftoss/types": "npm:8.6.8" + "@shapeshiftoss/caip": "npm:8.16.10" + "@shapeshiftoss/chain-adapters": "npm:11.5.1" + "@shapeshiftoss/swapper": "npm:20.0.0" + "@shapeshiftoss/types": "npm:9.0.0" "@shapeshiftoss/unchained-client": "npm:10.14.11" "@shapeshiftoss/unchained-pulumi": "npm:1.0.2" "@types/express": "npm:^5.0.5" @@ -19484,6 +19526,27 @@ __metadata: languageName: node linkType: hard +"viem@npm:2.55.11": + version: 2.55.11 + resolution: "viem@npm:2.55.11" + dependencies: + "@noble/curves": "npm:1.9.1" + "@noble/hashes": "npm:1.8.0" + "@scure/bip32": "npm:1.7.0" + "@scure/bip39": "npm:1.6.0" + abitype: "npm:1.2.3" + isows: "npm:1.0.7" + ox: "npm:0.14.33" + ws: "npm:8.21.0" + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/de0a6e4937c3f979803a6c550dfa9477aa30f0e26262ddb98f0d195d384c0546395e369cc5e7ac47409fdcbfb8c854676b53506acc883735d4778cf25a973163 + languageName: node + linkType: hard + "viem@npm:^2.22.19, viem@npm:^2.33.2": version: 2.54.1 resolution: "viem@npm:2.54.1" From 364e84c3294491f624230790c48af3c198f2b4cc Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Thu, 27 Aug 2026 16:11:41 -0600 Subject: [PATCH 2/4] fix(verification): keep FAILED semantics and read legacy metadata Two defects found by re-verifying every prod-snapshot row under 20.0.0 and diffing against the 17.8.0 baseline. The SDK's getSwapMetadata throws on a discriminator mismatch rather than returning undefined, and verifySwap converts any throw into a retryable PENDING. Using it directly turned "metadata missing" from a terminal FAILED into a swap re-verified every 30s forever. The `?.` and FAILED branches were dead code. tryGetSwapMetadata narrows without throwing so the terminal result is preserved. The jest mock returned undefined instead of throwing, so 'returns FAILED when relay metadata is missing' passed against a mock that did not match the SDK. The mock now throws like the real implementation. Rows predating the swapperMetadata migration store the flat keys and lost their affiliate data when re-verified: 17 rows went verified -> FAILED, wiping bps and verified amounts. They are terminal today so a deploy does not touch them, but a re-verification backfill would have destroyed them silently. Reads now fall back to the legacy keys. Verified by re-running all 37 rows: 0 regressions, every previously-verified row returns identical bps and amounts. The two 0x rows that go SUCCESS -> PENDING are outside the 200-trade analytics window, unrelated to the bump. Co-Authored-By: Claude Opus 5 (1M context) --- .../src/verification/__tests__/setup.ts | 6 +++-- .../verification/swap-verification.service.ts | 20 ++++++++++---- apps/swap-service/src/verification/utils.ts | 27 +++++++++++++++++++ 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/apps/swap-service/src/verification/__tests__/setup.ts b/apps/swap-service/src/verification/__tests__/setup.ts index 271ccdd..ac4f155 100644 --- a/apps/swap-service/src/verification/__tests__/setup.ts +++ b/apps/swap-service/src/verification/__tests__/setup.ts @@ -59,8 +59,10 @@ jest.mock('@shapeshiftoss/swapper', () => ({ Debridge: 'deBridge', }, assertGetCowNetwork: jest.fn(), - getSwapMetadata: (metadata: { name?: string } | undefined, name: string) => - metadata?.name === name ? metadata : undefined, + getSwapMetadata: (metadata: { name?: string } | undefined, name: string) => { + if (metadata?.name !== name) throw new Error(`Expected ${name} swap metadata`) + return metadata + }, getTreasuryAddressFromChainId: jest.fn(), relayTokenToAssetId: (token: { chainId: number; address: string }) => { const isNative = token.address.toLowerCase() === '0x0000000000000000000000000000000000000000' diff --git a/apps/swap-service/src/verification/swap-verification.service.ts b/apps/swap-service/src/verification/swap-verification.service.ts index 792f89d..3958d70 100644 --- a/apps/swap-service/src/verification/swap-verification.service.ts +++ b/apps/swap-service/src/verification/swap-verification.service.ts @@ -6,7 +6,6 @@ import { firstValueFrom } from 'rxjs' import { SwapVerificationResult } from '@shapeshift/shared-types' import { assertGetCowNetwork, - getSwapMetadata, getTreasuryAddressFromChainId, relayTokenToAssetId, SwapperName, @@ -32,7 +31,13 @@ import { ZrxApiResponse, ZrxTrade, } from './types' -import { applyBps, midgardToNativePrecision, noAffiliateResult } from './utils' +import { + applyBps, + getLegacySwapMetadata, + midgardToNativePrecision, + noAffiliateResult, + tryGetSwapMetadata, +} from './utils' @Injectable() export class SwapVerificationService { @@ -128,7 +133,9 @@ export class SwapVerificationService { private async verifyNearIntents(swap: Swap): Promise { const { metadata } = swap - const depositAddress = getSwapMetadata(metadata.swapperMetadata, 'nearIntents')?.depositAddress + const depositAddress = + tryGetSwapMetadata(metadata, 'nearIntents')?.depositAddress ?? + getLegacySwapMetadata(metadata).nearIntentsSpecific?.depositAddress if (!depositAddress) return noAffiliateResult('FAILED', 'Missing depositAddress in nearIntents metadata') const status = await OneClickService.getExecutionStatus(depositAddress) @@ -161,7 +168,9 @@ export class SwapVerificationService { private async verifyRelay(swap: Swap): Promise { const { metadata } = swap - const relayId = getSwapMetadata(metadata.swapperMetadata, 'relay')?.relayId + const relayId = + tryGetSwapMetadata(metadata, 'relay')?.relayId ?? + getLegacySwapMetadata(metadata).relayTransactionMetadata?.relayId if (!relayId) return noAffiliateResult('FAILED', 'Missing relayId in relay metadata') const { data } = await firstValueFrom( @@ -392,7 +401,8 @@ export class SwapVerificationService { } private async verifyChainflip(swap: Swap): Promise { - const chainflipSwapId = getSwapMetadata(swap.metadata.swapperMetadata, 'chainflip')?.swapId + const chainflipSwapId = + tryGetSwapMetadata(swap.metadata, 'chainflip')?.swapId ?? getLegacySwapMetadata(swap.metadata).chainflipSwapId if (!chainflipSwapId) return noAffiliateResult('FAILED', 'Missing swapId in chainflip metadata') diff --git a/apps/swap-service/src/verification/utils.ts b/apps/swap-service/src/verification/utils.ts index 61d4dfa..ea42e58 100644 --- a/apps/swap-service/src/verification/utils.ts +++ b/apps/swap-service/src/verification/utils.ts @@ -1,8 +1,35 @@ import type { SwapVerificationResult } from '@shapeshift/shared-types' import { bnOrZero } from '@shapeshiftoss/chain-adapters' +import type { SwapMetadata, SwapperMetadata } from '@shapeshiftoss/swapper' +import { getSwapMetadata } from '@shapeshiftoss/swapper' import type { MidgardCoin } from './types' +// Rows written before the swapperMetadata migration carry these flat keys instead. +type LegacySwapMetadata = { + relayTransactionMetadata?: { relayId?: string } + nearIntentsSpecific?: { depositAddress?: string } + chainflipSwapId?: string | number +} + +// getSwapMetadata throws on a discriminator mismatch, and verifySwap turns any throw into a +// retryable PENDING. Metadata never appears later, so narrow without throwing to keep FAILED. +export const tryGetSwapMetadata = ( + metadata: SwapMetadata, + name: T, +): Extract | undefined => { + try { + return getSwapMetadata(metadata.swapperMetadata, name) + } catch { + return undefined + } +} + +// Pre-migration rows are terminal today, so nothing re-reads them — but a re-verification +// backfill would wipe their affiliate data without this. +export const getLegacySwapMetadata = (metadata: SwapMetadata): LegacySwapMetadata => + metadata as unknown as LegacySwapMetadata + export const BPS_DENOMINATOR = 10000n export const MIDGARD_PRECISION = 8 From 3f4dbd6e01f914e455dbc9c4f8c18bcd15fba722 Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Fri, 28 Aug 2026 13:53:16 -0600 Subject: [PATCH 3/4] fix(verification): read swap metadata only from swapperMetadata Drops the legacy flat-key fallback in favour of a local non-throwing getSwapMetadata. Historical rows are normalized separately. Co-Authored-By: Claude Opus 5 (1M context) --- apps/swap-service/.env.example | 2 ++ .../verification/swap-verification.service.ts | 19 ++++------------ apps/swap-service/src/verification/utils.ts | 22 ++++--------------- 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/apps/swap-service/.env.example b/apps/swap-service/.env.example index 771dfe0..2ce1173 100644 --- a/apps/swap-service/.env.example +++ b/apps/swap-service/.env.example @@ -7,6 +7,8 @@ SERVICE_API_KEY= VITE_BEBOP_API_KEY= VITE_CHAINFLIP_API_KEY= VITE_NEAR_INTENTS_API_KEY= +VITE_RELAY_API_KEY= +VITE_ACROSS_API_KEY= # Database Configuration DATABASE_URL="postgresql://postgres:password@db:5432/microservices" diff --git a/apps/swap-service/src/verification/swap-verification.service.ts b/apps/swap-service/src/verification/swap-verification.service.ts index 3958d70..cf24b1e 100644 --- a/apps/swap-service/src/verification/swap-verification.service.ts +++ b/apps/swap-service/src/verification/swap-verification.service.ts @@ -31,13 +31,7 @@ import { ZrxApiResponse, ZrxTrade, } from './types' -import { - applyBps, - getLegacySwapMetadata, - midgardToNativePrecision, - noAffiliateResult, - tryGetSwapMetadata, -} from './utils' +import { applyBps, getSwapMetadata, midgardToNativePrecision, noAffiliateResult } from './utils' @Injectable() export class SwapVerificationService { @@ -133,9 +127,7 @@ export class SwapVerificationService { private async verifyNearIntents(swap: Swap): Promise { const { metadata } = swap - const depositAddress = - tryGetSwapMetadata(metadata, 'nearIntents')?.depositAddress ?? - getLegacySwapMetadata(metadata).nearIntentsSpecific?.depositAddress + const depositAddress = getSwapMetadata(metadata, 'nearIntents')?.depositAddress if (!depositAddress) return noAffiliateResult('FAILED', 'Missing depositAddress in nearIntents metadata') const status = await OneClickService.getExecutionStatus(depositAddress) @@ -168,9 +160,7 @@ export class SwapVerificationService { private async verifyRelay(swap: Swap): Promise { const { metadata } = swap - const relayId = - tryGetSwapMetadata(metadata, 'relay')?.relayId ?? - getLegacySwapMetadata(metadata).relayTransactionMetadata?.relayId + const relayId = getSwapMetadata(metadata, 'relay')?.relayId if (!relayId) return noAffiliateResult('FAILED', 'Missing relayId in relay metadata') const { data } = await firstValueFrom( @@ -401,8 +391,7 @@ export class SwapVerificationService { } private async verifyChainflip(swap: Swap): Promise { - const chainflipSwapId = - tryGetSwapMetadata(swap.metadata, 'chainflip')?.swapId ?? getLegacySwapMetadata(swap.metadata).chainflipSwapId + const chainflipSwapId = getSwapMetadata(swap.metadata, 'chainflip')?.swapId if (!chainflipSwapId) return noAffiliateResult('FAILED', 'Missing swapId in chainflip metadata') diff --git a/apps/swap-service/src/verification/utils.ts b/apps/swap-service/src/verification/utils.ts index ea42e58..a98a783 100644 --- a/apps/swap-service/src/verification/utils.ts +++ b/apps/swap-service/src/verification/utils.ts @@ -1,35 +1,21 @@ import type { SwapVerificationResult } from '@shapeshift/shared-types' import { bnOrZero } from '@shapeshiftoss/chain-adapters' import type { SwapMetadata, SwapperMetadata } from '@shapeshiftoss/swapper' -import { getSwapMetadata } from '@shapeshiftoss/swapper' +import * as swapper from '@shapeshiftoss/swapper' import type { MidgardCoin } from './types' -// Rows written before the swapperMetadata migration carry these flat keys instead. -type LegacySwapMetadata = { - relayTransactionMetadata?: { relayId?: string } - nearIntentsSpecific?: { depositAddress?: string } - chainflipSwapId?: string | number -} - -// getSwapMetadata throws on a discriminator mismatch, and verifySwap turns any throw into a -// retryable PENDING. Metadata never appears later, so narrow without throwing to keep FAILED. -export const tryGetSwapMetadata = ( +export const getSwapMetadata = ( metadata: SwapMetadata, name: T, ): Extract | undefined => { try { - return getSwapMetadata(metadata.swapperMetadata, name) + return swapper.getSwapMetadata(metadata.swapperMetadata, name) } catch { - return undefined + return } } -// Pre-migration rows are terminal today, so nothing re-reads them — but a re-verification -// backfill would wipe their affiliate data without this. -export const getLegacySwapMetadata = (metadata: SwapMetadata): LegacySwapMetadata => - metadata as unknown as LegacySwapMetadata - export const BPS_DENOMINATOR = 10000n export const MIDGARD_PRECISION = 8 From 770e7bd66397c50edc2bf0c98bf5c1602895834b Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Fri, 28 Aug 2026 14:03:26 -0600 Subject: [PATCH 4/4] fix(swap-service): pass the Relay and Across API keys through to the swapper Both were placeholder empty strings since the swapper bump made them required. Co-Authored-By: Claude Opus 5 (1M context) --- apps/swap-service/src/env.ts | 2 ++ apps/swap-service/src/swaps/swapper-config.ts | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/swap-service/src/env.ts b/apps/swap-service/src/env.ts index 749d489..09965e6 100644 --- a/apps/swap-service/src/env.ts +++ b/apps/swap-service/src/env.ts @@ -17,6 +17,8 @@ const schema = z.object({ VITE_BEBOP_API_KEY: z.string().min(1), VITE_NEAR_INTENTS_API_KEY: z.string().min(1), VITE_CHAINFLIP_API_KEY: z.string().min(1), + VITE_RELAY_API_KEY: z.string().min(1), + VITE_ACROSS_API_KEY: z.string().min(1), // Database DATABASE_URL: z.string().min(1), diff --git a/apps/swap-service/src/swaps/swapper-config.ts b/apps/swap-service/src/swaps/swapper-config.ts index f55db51..5389bb7 100644 --- a/apps/swap-service/src/swaps/swapper-config.ts +++ b/apps/swap-service/src/swaps/swapper-config.ts @@ -13,7 +13,7 @@ import type { TronChainAdapterService } from '../lib/chain-adapters/tron.service import type { UtxoChainAdapterService } from '../lib/chain-adapters/utxo.service' export const getSwapperConfig = (): SwapperConfig => ({ - VITE_ACROSS_API_KEY: '', + VITE_ACROSS_API_KEY: env.VITE_ACROSS_API_KEY, VITE_ACROSS_API_URL: env.VITE_ACROSS_API_URL, VITE_ACROSS_INTEGRATOR_ID: '', VITE_BEBOP_API_KEY: env.VITE_BEBOP_API_KEY, @@ -29,7 +29,7 @@ export const getSwapperConfig = (): SwapperConfig => ({ VITE_MAYACHAIN_NODE_URL: env.VITE_MAYACHAIN_NODE_URL, VITE_NEAR_INTENTS_API_KEY: env.VITE_NEAR_INTENTS_API_KEY, VITE_PORTALS_BASE_URL: env.VITE_PORTALS_BASE_URL, - VITE_RELAY_API_KEY: '', + VITE_RELAY_API_KEY: env.VITE_RELAY_API_KEY, VITE_RELAY_API_URL: env.VITE_RELAY_API_URL, VITE_SUI_NODE_URL: env.VITE_SUI_NODE_URL, VITE_THORCHAIN_MIDGARD_URL: env.VITE_THORCHAIN_MIDGARD_URL,