From 69a4e7c681113a2d849a849f8ebb2264492ff195 Mon Sep 17 00:00:00 2001 From: Priyajeet Hora Date: Thu, 11 Sep 2025 13:53:45 -0700 Subject: [PATCH 1/3] fix: remove double quoting --- .gitignore | 1 - .vscode/settings.json | 10 ++ src/hooks/useConfirmTransaction.ts | 23 ++- src/hooks/useFetchQuotes.ts | 5 +- src/routes/mayan/index.ts | 8 +- src/routes/operator.ts | 265 ++++++++++++++++++----------- src/routes/sdkv2/index.ts | 3 - src/routes/sdkv2/route.ts | 65 +++---- src/routes/types.ts | 7 + src/utils/routes.ts | 19 +++ 10 files changed, 248 insertions(+), 158 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 src/routes/sdkv2/index.ts create mode 100644 src/routes/types.ts diff --git a/.gitignore b/.gitignore index f951efb0b..e868485d9 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,6 @@ tsconfig.tsbuildinfo # Local Netlify folder .netlify -.vscode .ignore .yalc yalc.lock diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..7ed67eb07 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "prettier.requireConfig": true, + "workbench.colorCustomizations": { + "activityBar.background": "#02072d", + "titleBar.activeBackground": "#000285", + "titleBar.activeForeground": "#dae6f9" + } +} diff --git a/src/hooks/useConfirmTransaction.ts b/src/hooks/useConfirmTransaction.ts index 30032afa3..07c437edf 100644 --- a/src/hooks/useConfirmTransaction.ts +++ b/src/hooks/useConfirmTransaction.ts @@ -161,18 +161,17 @@ const useConfirmTransaction = (props: Props): ReturnProps => { ); } - const [sdkRoute, receipt] = await config.routes - .get(route) - .send( - sourceToken, - amount, - sourceChain, - signer, - destChain, - receivingWallet.address, - destToken, - { nativeGas: toNativeToken }, - ); + const [sdkRoute, receipt] = await config.routes.execute( + route, + sourceToken, + amount, + sourceChain, + signer, + destChain, + receivingWallet.address, + destToken, + { nativeGas: toNativeToken }, + ); // Clear cached balances on sending chain clearBalanceCache(sendingWallet, sourceChain); diff --git a/src/hooks/useFetchQuotes.ts b/src/hooks/useFetchQuotes.ts index dc3e527ad..14fb193fe 100644 --- a/src/hooks/useFetchQuotes.ts +++ b/src/hooks/useFetchQuotes.ts @@ -83,7 +83,10 @@ export default (routes: string[], params: Params): HookReturn => { return; } - const nextExpiry = config.routes.quoteCache.nextExpiry(routes, rParams); + const nextExpiry = config.routes.quoteMetadataCache.nextExpiry( + routes, + rParams, + ); if (!nextExpiry) { return; diff --git a/src/routes/mayan/index.ts b/src/routes/mayan/index.ts index fcbd99072..0f38d640e 100644 --- a/src/routes/mayan/index.ts +++ b/src/routes/mayan/index.ts @@ -611,9 +611,11 @@ class MayanRouteBase extends routes.AutomaticRoute< ), }; - const deadline64Seconds = parseInt(quote.deadline64, 10) * 1000; - const expires = deadline64Seconds - ? new Date(deadline64Seconds) + // deadline64 is in seconds + const deadline64MilliSeconds = parseInt(quote.deadline64, 10) * 1000; + + const expires = deadline64MilliSeconds + ? new Date(deadline64MilliSeconds) : undefined; const fullQuote: Q = { diff --git a/src/routes/operator.ts b/src/routes/operator.ts index d16296198..3bf3cf0e9 100644 --- a/src/routes/operator.ts +++ b/src/routes/operator.ts @@ -4,10 +4,18 @@ import { parseTokenKey, tokenKey } from 'config/tokens'; import { maybeLogSdkError } from 'utils/errors'; import memoize from 'fast-memoize'; -import type { Chain, TransactionId, TokenId } from '@wormhole-foundation/sdk'; +import type { + Chain, + TransactionId, + TokenId, + Network, + Signer, +} from '@wormhole-foundation/sdk'; import { routes, amount as sdkAmount } from '@wormhole-foundation/sdk'; -import SDKv2Route from './sdkv2'; +import SDKv2Route from './sdkv2/route'; +import type { QuoteMetadata } from './types'; +import { getDefaultQuoteExpiry, getQuoteExpiry } from 'utils/routes'; export interface TxInfo { route: string; @@ -39,7 +47,7 @@ export interface QuoteParams { export default class RouteOperator { preference: string[]; routes: Record; - quoteCache: QuoteCache; + quoteMetadataCache: QuoteMetadataCache; constructor(routesConfig: routes.RouteConstructor[] = DEFAULT_ROUTES) { const routes = {}; @@ -56,7 +64,7 @@ export default class RouteOperator { } this.routes = routes; this.preference = preference; - this.quoteCache = new QuoteCache(); + this.quoteMetadataCache = new QuoteMetadataCache(); } get(name: string): SDKv2Route { @@ -168,24 +176,29 @@ export default class RouteOperator { async getQuotes( routes: string[], params: QuoteParams, - ): Promise>> { + ): Promise> { const results = await Promise.allSettled( routes.map((route) => { - const cachedResult = this.quoteCache.get(route, params); - if (cachedResult) { - return cachedResult; - } else { - return this.quoteCache.fetch(route, params, this.get(route)); + const quoteMetadata = this.quoteMetadataCache.get(route, params); + + if (quoteMetadata?.quote) { + return quoteMetadata.quote; } + + return this.quoteMetadataCache.fetchQuote( + route, + params, + this.get(route), + ); }), ); - // Convert the array of promise results to a quoteName=>quoteResult map - const quotes = {}; + const quotes: Record = {}; for (let i = 0; i < routes.length; i++) { const route = routes[i]; const result = results[i]; + if (result.status === 'rejected') { quotes[route] = { success: false, @@ -215,17 +228,69 @@ export default class RouteOperator { return isSupported; }); + + async execute( + routeName: string, + sourceToken: Token, + amount: sdkAmount.Amount, + sourceChain: Chain, + signer: Signer, + destChain: Chain, + recipientAddress: string, + destToken: Token, + options: routes.AutomaticTokenBridgeRoute.Options, + ) { + const route = this.get(routeName); + + const quoteParams: QuoteParams = { + amount, + sourceChain, + sourceToken, + destChain, + destToken, + nativeGas: options.nativeGas, + recipient: recipientAddress, + }; + + let quoteMetadata = this.quoteMetadataCache.get(routeName, quoteParams); + + const isRefetchRequired = + !quoteMetadata?.quote || + !quoteMetadata?.request || + !quoteMetadata?.router; + + if (isRefetchRequired) { + // Refetch the quote as cached quote does not exist + // This case should be hit rarely + quoteMetadata = await route.getQuote( + amount, + sourceToken, + destToken, + sourceChain, + destChain, + options, + recipientAddress, + ); + } + + return route.send( + quoteMetadata as QuoteMetadata, + signer, + destChain, + recipientAddress, + ); + } } // This caches successful quote results from SDK routes and handles multiple concurrent // async functions asking for the same quote gracefully. // // If we are already fetching a quote and a second hook requests the same quote elsewhere, -// we queue up a Promise in `QuoteCacheEntry.pending` that we resolve when the original +// we queue up a Promise in `QuoteMetadataEntry.pending` that we resolve when the original // quote request is resolved. This just prevents us from making redundant API calls when // multiple components or hooks are interested in a quote. -class QuoteCache { - cache: Record; +class QuoteMetadataCache { + cache: Record; pending: Record; constructor() { @@ -243,96 +308,84 @@ class QuoteCache { )}:${params.nativeGas}:${params.recipient}`; } - get(routeName: string, params: QuoteParams): QuoteResult | null { + get(routeName: string, params: QuoteParams): Partial | null { const key = this.quoteParamsKey(routeName, params); - const cachedVal = this.cache[key]; - if (cachedVal) { - if (cachedVal.ttl() > 5) { - return cachedVal.result; - } else { - delete this.cache[key]; - } + const quoteMetadata = this.cache[key]; + const hasQuoteExpired = quoteMetadata?.ttl() <= 5_000; + + if (!quoteMetadata || hasQuoteExpired) { + delete this.cache[key]; + return null; } - return null; + return { + quote: quoteMetadata.quote, + request: quoteMetadata.request, + router: quoteMetadata.router, + }; } - async fetch( + async fetchQuote( routeName: string, params: QuoteParams, route: SDKv2Route, ): Promise { - console.debug('Fetching quote', routeName, params); - const key = this.quoteParamsKey(routeName, params); const pending = this.pending[key]; + if (pending) { // We already have a pending request for this key, so don't create a new one. // Instead, subscribe to its result when it resolves return new Promise((resolve, reject) => { pending.push({ resolve, reject }); }); - } else { - // Initialize list of promises awaiting this result - const returnPromise: Promise = new Promise( - (resolve, reject) => { - this.pending[key] = [{ resolve, reject }]; - }, - ); + } - // We don't yet have a pending request for this key, so initiate one - route - .computeQuote( - params.amount, - params.sourceToken, - params.destToken, - params.sourceChain, - params.destChain, - { nativeGas: params.nativeGas }, - params.recipient, - ) - .then((result: QuoteResult) => { - const pending = this.pending[key]; - for (const { resolve } of pending) { - resolve(result); - } - delete this.pending[key]; - - if (result.success) { - const now = Date.now(); - - // A valid expiry should be at least 5 seconds in the future - // and if not, we should default to 60 seconds. - const isValidExpiry = - result.expires instanceof Date && - result.expires.getTime() > now + 5_000; - - if (!isValidExpiry) { - result.expires = new Date(now + 60_000); - } - } - - console.debug(`Fetched quote`, routeName, result); - - this.cache[key] = new QuoteCacheEntry(result); - }) - .catch((err: any) => { - console.debug(`Error fetching quote`, routeName, err); - const pending = this.pending[key]; - for (const { reject } of pending) { - reject(err); - } - delete this.pending[key]; - - // Cache uncaught error - this.cache[key] = new QuoteCacheEntry({ - success: false, - error: err, - }); + // We don't yet have a pending request for this key, so initiate one + route + .computeQuote( + params.amount, + params.sourceToken, + params.destToken, + params.sourceChain, + params.destChain, + { nativeGas: params.nativeGas }, + params.recipient, + ) + .then(({ router, quote, request }: Required) => { + const pending = this.pending[key]; + + for (const { resolve } of pending) { + resolve(quote); + } + + delete this.pending[key]; + + if (quote.success) { + quote.expires = getQuoteExpiry(quote.expires); + } + + this.cache[key] = new QuoteMetadataEntry(quote, router, request); + }) + .catch((err: any) => { + const pending = this.pending[key]; + + for (const { reject } of pending) { + reject(err); + } + + delete this.pending[key]; + + // Cache uncaught error + this.cache[key] = new QuoteMetadataEntry({ + success: false, + error: err, }); + }); - return returnPromise; - } + return new Promise((resolve, reject) => { + this.pending[key] = [{ resolve, reject }]; + }); } nextExpiry(routes: string[], params: QuoteParams): Date | undefined { @@ -359,31 +412,41 @@ interface QuotePromiseHandlers { reject: (err: Error) => void; } -class QuoteCacheEntry { - // Last quote we received (the cached value) - result: QuoteResult; +class QuoteMetadataEntry { + // Last quote we received + quote: QuoteResult; // Last time we fetched a quote timestamp: Date; - - constructor(result: QuoteResult) { - this.result = result; + // Optional route used for the quote + router?: routes.Route; + // Optional request used for the quote + request?: routes.RouteTransferRequest; + + constructor( + quote: QuoteResult, + router?: routes.Route, + request?: routes.RouteTransferRequest, + ) { + this.quote = quote; + this.router = router; + this.request = request; this.timestamp = new Date(); } // Number of seconds this quote is still valid for before we should fetch a new one - expires(): Date { - if (this.result.success) { - // For a successful quote, if it specifies an expiry we return that - // otherwise we default to a TTL 1 minute - return this.result.expires ?? new Date(this.timestamp.valueOf() + 60_000); + expires() { + if (this.quote.success) { + return ( + this.quote.expires ?? getDefaultQuoteExpiry(this.timestamp.getTime()) + ); } else { - // We cache errors for 10 seconds - return new Date(this.timestamp.valueOf() + 120_000); + // We cache errors for 120 seconds + return new Date(this.timestamp.getTime() + 120_000); } } - // TTL in seconds before quote expires - ttl(): number { - return (this.expires().valueOf() - Date.now().valueOf()) / 1000; + // TTL in ms before quote expires + ttl() { + return this.expires().getTime() - Date.now(); } } diff --git a/src/routes/sdkv2/index.ts b/src/routes/sdkv2/index.ts deleted file mode 100644 index 178ddbeb6..000000000 --- a/src/routes/sdkv2/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { SDKv2Route } from './route'; - -export default SDKv2Route; diff --git a/src/routes/sdkv2/route.ts b/src/routes/sdkv2/route.ts index 25c465733..a8b654be4 100644 --- a/src/routes/sdkv2/route.ts +++ b/src/routes/sdkv2/route.ts @@ -25,11 +25,12 @@ import { getWrappedNativeToken, shouldFilterSameChainToken, } from 'utils/wrappedNativeTokens'; +import type { QuoteMetadata } from '../types'; type Amount = sdkAmount.Amount; // =^o^= -export class SDKv2Route { +export default class SDKv2Route { // TODO: remove this IS_TOKEN_BRIDGE_ROUTE = false; @@ -178,14 +179,8 @@ export class SDKv2Route { destChain: Chain, options?: routes.AutomaticTokenBridgeRoute.Options, recipient?: string, - ): Promise< - [ - routes.Route, - routes.QuoteResult, - routes.RouteTransferRequest, - ] - > { - const req = await this.createRequest( + ): Promise { + const request = await this.createRequest( sourceToken, destToken, sourceChain, @@ -193,9 +188,9 @@ export class SDKv2Route { recipient, ); - const wh = await getWormholeContextV2(); - const route = new this.rc(wh); - const validationResult = await route.validate(req, { + const router = await this.createRouter(); + + const validationResult = await router.validate(request, { amount: sdkAmount.display(amount), options, }); @@ -204,9 +199,16 @@ export class SDKv2Route { throw validationResult.error; } - const quote = await route.quote(req, validationResult.params); + const quote = await router.quote(request, validationResult.params); - return [route, quote, req]; + return { router, quote, request }; + } + + async createRouter() { + const wh = await getWormholeContextV2(); + const router = new this.rc(wh); + + return router; } async createRequest( @@ -218,8 +220,8 @@ export class SDKv2Route { ): Promise> { const sourceContext = (await this.getV2ChainContext(sourceChain)).context; const destContext = (await this.getV2ChainContext(destChain)).context; - const wh = await getWormholeContextV2(); + const req = await routes.RouteTransferRequest.create( wh, /* @ts-ignore */ @@ -233,6 +235,7 @@ export class SDKv2Route { sourceContext, destContext, ); + return req; } @@ -244,12 +247,12 @@ export class SDKv2Route { toChain: Chain, options?: routes.AutomaticTokenBridgeRoute.Options, recipient?: string, - ): Promise> { + ): Promise { if (!fromChain || !toChain) { throw new Error('Need both chains to get a quote from SDKv2'); } - const [, quote] = await this.getQuote( + const { router, quote, request } = await this.getQuote( amountIn, sourceToken, destToken, @@ -263,35 +266,23 @@ export class SDKv2Route { throw quote.error; } - return quote; + return { router, quote, request }; } async send( - sourceToken: Token, - amount: Amount, - fromChain: Chain, + quoteMetadata: QuoteMetadata, signer: Signer, toChain: Chain, recipientAddress: string, - destToken: Token, - options?: routes.AutomaticTokenBridgeRoute.Options, ): Promise<[routes.Route, routes.Receipt]> { - const [route, quote, req] = await this.getQuote( - amount, - sourceToken, - destToken, - fromChain, - toChain, - options, - recipientAddress, - ); + const { quote, router, request } = quoteMetadata; if (!quote.success) { throw quote.error; } - let receipt = await route.initiate( - req, + let receipt = await router.initiate( + request, signer, quote, Wormhole.chainAddress(toChain, recipientAddress), @@ -303,7 +294,7 @@ export class SDKv2Route { receipt.state === TransferState.SourceInitiated || receipt.state === TransferState.SourceFinalized ) { - return [route, receipt]; + return [router, receipt]; } // Otherwise track the transfer until it reaches a final state, @@ -314,9 +305,9 @@ export class SDKv2Route { while (retries < maxRetries) { try { - for await (receipt of route.track(receipt, 120 * 1000)) { + for await (receipt of router.track(receipt, 120 * 1000)) { if (receipt.state >= TransferState.SourceInitiated) { - return [route, receipt]; + return [router, receipt]; } } } catch (e) { diff --git a/src/routes/types.ts b/src/routes/types.ts new file mode 100644 index 000000000..2aad0e047 --- /dev/null +++ b/src/routes/types.ts @@ -0,0 +1,7 @@ +import type { routes, Network } from '@wormhole-foundation/sdk'; + +export type QuoteMetadata = { + router: routes.Route; + quote: routes.QuoteResult; + request: routes.RouteTransferRequest; +}; diff --git a/src/utils/routes.ts b/src/utils/routes.ts index 250a74079..e69c5647f 100644 --- a/src/utils/routes.ts +++ b/src/utils/routes.ts @@ -35,3 +35,22 @@ export const getBestRoutes = ( return { fastestRoute, cheapestRoute }; }; + +export function getDefaultQuoteExpiry(fromDate: number) { + return new Date(fromDate + 30_000); +} + +export function getQuoteExpiry(expiryFromQuote?: Date) { + const now = Date.now(); + + // A valid expiry should be at least 5 seconds in the future + // If not, we should default it to 30 seconds. + const isValidExpiry = + expiryFromQuote instanceof Date && expiryFromQuote.getTime() > now + 5_000; + + if (!isValidExpiry) { + return getDefaultQuoteExpiry(now); + } + + return expiryFromQuote; +} From 2ab17dbdc719f4241acea8e37ce8ee30a6bc4152 Mon Sep 17 00:00:00 2001 From: Priyajeet Hora Date: Thu, 11 Sep 2025 15:36:33 -0700 Subject: [PATCH 2/3] fix: cleanup --- src/routes/operator.ts | 48 ++++++++++++++------------------ src/routes/sdkv2/route.ts | 58 +++++++++++---------------------------- src/routes/types.ts | 2 +- 3 files changed, 37 insertions(+), 71 deletions(-) diff --git a/src/routes/operator.ts b/src/routes/operator.ts index 3bf3cf0e9..74fc7d0a5 100644 --- a/src/routes/operator.ts +++ b/src/routes/operator.ts @@ -254,14 +254,7 @@ export default class RouteOperator { let quoteMetadata = this.quoteMetadataCache.get(routeName, quoteParams); - const isRefetchRequired = - !quoteMetadata?.quote || - !quoteMetadata?.request || - !quoteMetadata?.router; - - if (isRefetchRequired) { - // Refetch the quote as cached quote does not exist - // This case should be hit rarely + if (!quoteMetadata) { quoteMetadata = await route.getQuote( amount, sourceToken, @@ -273,12 +266,7 @@ export default class RouteOperator { ); } - return route.send( - quoteMetadata as QuoteMetadata, - signer, - destChain, - recipientAddress, - ); + return route.send(quoteMetadata, signer, destChain, recipientAddress); } } @@ -308,7 +296,7 @@ class QuoteMetadataCache { )}:${params.nativeGas}:${params.recipient}`; } - get(routeName: string, params: QuoteParams): Partial | null { + get(routeName: string, params: QuoteParams): QuoteMetadata | null { const key = this.quoteParamsKey(routeName, params); const quoteMetadata = this.cache[key]; const hasQuoteExpired = quoteMetadata?.ttl() <= 5_000; @@ -321,7 +309,7 @@ class QuoteMetadataCache { return { quote: quoteMetadata.quote, request: quoteMetadata.request, - router: quoteMetadata.router, + routeInstance: quoteMetadata.routeInstance, }; } @@ -343,7 +331,7 @@ class QuoteMetadataCache { // We don't yet have a pending request for this key, so initiate one route - .computeQuote( + .getQuote( params.amount, params.sourceToken, params.destToken, @@ -352,7 +340,7 @@ class QuoteMetadataCache { { nativeGas: params.nativeGas }, params.recipient, ) - .then(({ router, quote, request }: Required) => { + .then(({ routeInstance, quote, request }: QuoteMetadata) => { const pending = this.pending[key]; for (const { resolve } of pending) { @@ -365,7 +353,7 @@ class QuoteMetadataCache { quote.expires = getQuoteExpiry(quote.expires); } - this.cache[key] = new QuoteMetadataEntry(quote, router, request); + this.cache[key] = new QuoteMetadataEntry(quote, routeInstance, request); }) .catch((err: any) => { const pending = this.pending[key]; @@ -377,10 +365,14 @@ class QuoteMetadataCache { delete this.pending[key]; // Cache uncaught error - this.cache[key] = new QuoteMetadataEntry({ - success: false, - error: err, - }); + this.cache[key] = new QuoteMetadataEntry( + { + success: false, + error: err, + }, + {} as routes.Route, + {} as routes.RouteTransferRequest, + ); }); return new Promise((resolve, reject) => { @@ -418,17 +410,17 @@ class QuoteMetadataEntry { // Last time we fetched a quote timestamp: Date; // Optional route used for the quote - router?: routes.Route; + routeInstance: routes.Route; // Optional request used for the quote - request?: routes.RouteTransferRequest; + request: routes.RouteTransferRequest; constructor( quote: QuoteResult, - router?: routes.Route, - request?: routes.RouteTransferRequest, + routeInstance: routes.Route, + request: routes.RouteTransferRequest, ) { this.quote = quote; - this.router = router; + this.routeInstance = routeInstance; this.request = request; this.timestamp = new Date(); } diff --git a/src/routes/sdkv2/route.ts b/src/routes/sdkv2/route.ts index a8b654be4..7fa9b7979 100644 --- a/src/routes/sdkv2/route.ts +++ b/src/routes/sdkv2/route.ts @@ -180,6 +180,10 @@ export default class SDKv2Route { options?: routes.AutomaticTokenBridgeRoute.Options, recipient?: string, ): Promise { + if (!sourceChain || !destChain || !sourceToken || !destToken) { + throw new Error('Malformed quote request'); + } + const request = await this.createRequest( sourceToken, destToken, @@ -188,9 +192,9 @@ export default class SDKv2Route { recipient, ); - const router = await this.createRouter(); + const routeInstance = await this.createRouteInstance(); - const validationResult = await router.validate(request, { + const validationResult = await routeInstance.validate(request, { amount: sdkAmount.display(amount), options, }); @@ -199,16 +203,16 @@ export default class SDKv2Route { throw validationResult.error; } - const quote = await router.quote(request, validationResult.params); + const quote = await routeInstance.quote(request, validationResult.params); - return { router, quote, request }; + return { routeInstance, quote, request }; } - async createRouter() { + async createRouteInstance() { const wh = await getWormholeContextV2(); - const router = new this.rc(wh); + const routeInstance = new this.rc(wh); - return router; + return routeInstance; } async createRequest( @@ -239,49 +243,19 @@ export default class SDKv2Route { return req; } - async computeQuote( - amountIn: Amount, - sourceToken: Token, - destToken: Token, - fromChain: Chain, - toChain: Chain, - options?: routes.AutomaticTokenBridgeRoute.Options, - recipient?: string, - ): Promise { - if (!fromChain || !toChain) { - throw new Error('Need both chains to get a quote from SDKv2'); - } - - const { router, quote, request } = await this.getQuote( - amountIn, - sourceToken, - destToken, - fromChain, - toChain, - options, - recipient, - ); - - if (!quote.success) { - throw quote.error; - } - - return { router, quote, request }; - } - async send( quoteMetadata: QuoteMetadata, signer: Signer, toChain: Chain, recipientAddress: string, ): Promise<[routes.Route, routes.Receipt]> { - const { quote, router, request } = quoteMetadata; + const { quote, routeInstance, request } = quoteMetadata; if (!quote.success) { throw quote.error; } - let receipt = await router.initiate( + let receipt = await routeInstance.initiate( request, signer, quote, @@ -294,7 +268,7 @@ export default class SDKv2Route { receipt.state === TransferState.SourceInitiated || receipt.state === TransferState.SourceFinalized ) { - return [router, receipt]; + return [routeInstance, receipt]; } // Otherwise track the transfer until it reaches a final state, @@ -305,9 +279,9 @@ export default class SDKv2Route { while (retries < maxRetries) { try { - for await (receipt of router.track(receipt, 120 * 1000)) { + for await (receipt of routeInstance.track(receipt, 120 * 1000)) { if (receipt.state >= TransferState.SourceInitiated) { - return [router, receipt]; + return [routeInstance, receipt]; } } } catch (e) { diff --git a/src/routes/types.ts b/src/routes/types.ts index 2aad0e047..4795e2fcb 100644 --- a/src/routes/types.ts +++ b/src/routes/types.ts @@ -1,7 +1,7 @@ import type { routes, Network } from '@wormhole-foundation/sdk'; export type QuoteMetadata = { - router: routes.Route; quote: routes.QuoteResult; request: routes.RouteTransferRequest; + routeInstance: routes.Route; }; From a84e6c4a3b7a7c26da5bf3b039b3b903119d5206 Mon Sep 17 00:00:00 2001 From: Priyajeet Hora Date: Mon, 15 Sep 2025 16:38:55 -0700 Subject: [PATCH 3/3] fix: Terms of service --- src/views/Terms.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/views/Terms.tsx b/src/views/Terms.tsx index fbd793582..44a3d63f4 100644 --- a/src/views/Terms.tsx +++ b/src/views/Terms.tsx @@ -30,13 +30,12 @@ function Terms() { -

Last Updated: April 18, 2025

+

Last Updated: September 15, 2025

These Terms of Service (the "Agreement") explain the terms and conditions by which you may access and use the Services provided by - W7, LLC (d.b.a. Wormhole Labs) (the "Company," "we," "us," or "our"). - The "Services" shall include, but not limited to, the website located - at{' '} + Maha Labs, LLC (the "Company," "we," "us," or "our"). The "Services" + shall include, but not limited to, the website located at{' '}