diff --git a/packages/public-api/src/lib/quoteStore.ts b/packages/public-api/src/lib/quoteStore.ts index caae711f67f..60a3e8ee798 100644 --- a/packages/public-api/src/lib/quoteStore.ts +++ b/packages/public-api/src/lib/quoteStore.ts @@ -16,7 +16,7 @@ export type StoredQuote = { createdAt: number expiresAt: number metadata: { - chainflipSwapId?: number + chainflipSwapId?: number | string nearIntentsDepositAddress?: string nearIntentsDepositMemo?: string relayId?: string diff --git a/packages/swapper/src/swappers/ChainflipSwapper/endpoints.ts b/packages/swapper/src/swappers/ChainflipSwapper/endpoints.ts index 406015fcb09..17858beb28b 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/endpoints.ts @@ -169,6 +169,10 @@ export const chainflipApi: SwapperApi = { chainSpecific: { from, tokenId }, }) + // Chainflip deposit addresses are program-owned accounts that require more compute + // than a regular SOL transfer. Apply a safety margin to avoid "Computational budget exceeded". + const computeUnits = Math.ceil(Math.max(Number(fast.chainSpecific.computeUnits), 50_000) * 1.6) + return adapter.buildSendApiTransaction({ to, from, @@ -176,7 +180,7 @@ export const chainflipApi: SwapperApi = { accountNumber, chainSpecific: { tokenId, - computeUnitLimit: fast.chainSpecific.computeUnits, + computeUnitLimit: String(computeUnits), computeUnitPrice: fast.chainSpecific.priorityFee, }, }) @@ -207,11 +211,17 @@ export const chainflipApi: SwapperApi = { }, }) - return fast.txFee + // Mirror the same compute budget boost as getUnsignedSolanaTransaction + // to avoid underquoting fees for Chainflip deposit addresses + const simulatedUnits = Number(fast.chainSpecific.computeUnits) + const boostedUnits = Math.ceil(Math.max(simulatedUnits, 50_000) * 1.6) + const ratio = simulatedUnits > 0 ? boostedUnits / simulatedUnits : 1 + + return String(Math.ceil(Number(fast.txFee) * ratio)) }, checkTradeStatus: async ({ config, swap }) => { const chainflipSwapId = swap?.metadata.chainflipSwapId - if (!chainflipSwapId) throw Error(`chainflipSwapId is required`) + if (chainflipSwapId == null) throw Error(`chainflipSwapId is required`) const brokerUrl = config.VITE_CHAINFLIP_API_URL const apiKey = config.VITE_CHAINFLIP_API_KEY @@ -231,7 +241,7 @@ export const chainflipApi: SwapperApi = { const { data: statusResponse } = maybeStatusResponse.unwrap() const { - status: { swapEgress }, + status: { swapEgress, swapId }, } = statusResponse // Assume no outbound Tx is a pending Tx @@ -240,6 +250,7 @@ export const chainflipApi: SwapperApi = { buyTxHash: undefined, status: TxStatus.Pending, message: getLatestChainflipStatusMessage(statusResponse), + chainflipSwapId: swapId ?? undefined, } } @@ -249,6 +260,7 @@ export const chainflipApi: SwapperApi = { buyTxHash: swapEgress.transactionReference, status: TxStatus.Confirmed, message: undefined, + chainflipSwapId: swapId ?? undefined, } }, } diff --git a/packages/swapper/src/swappers/ChainflipSwapper/types.ts b/packages/swapper/src/swappers/ChainflipSwapper/types.ts index 89cc43d67ae..4f40b298b78 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/types.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/types.ts @@ -5,6 +5,7 @@ import type { ChainflipBaasStatusSwap } from './models/ChainflipBaasStatusSwap' export type ChainFlipStatus = { status: { state: 'waiting' | 'receiving' | 'swapping' | 'sending' | 'sent' | 'completed' | 'failed' + swapId?: string swap?: ChainflipBaasStatusSwap swapEgress?: ChainflipBaasStatusEgress } diff --git a/packages/swapper/src/types.ts b/packages/swapper/src/types.ts index e5c1f785c7a..f8bb33adc81 100644 --- a/packages/swapper/src/types.ts +++ b/packages/swapper/src/types.ts @@ -448,7 +448,7 @@ export type TradeQuoteStep = { } cowswapQuoteResponse?: OrderQuoteResponse chainflipSpecific?: { - chainflipSwapId?: number + chainflipSwapId?: number | string chainflipDepositAddress?: string chainflipNumberOfChunks?: number chainflipChunkIntervalBlocks?: number @@ -567,7 +567,7 @@ export type SwapExecutionMetadata = { } export type SwapperSpecificMetadata = { - chainflipSwapId: number | undefined + chainflipSwapId: number | string | undefined nearIntentsSpecific?: { depositAddress: string depositMemo?: string @@ -821,6 +821,7 @@ export type TradeStatus = { relayerExplorerTxLink?: string | undefined message: string | [string, InterpolationOptions] | undefined actualBuyAmountCryptoBaseUnit?: string + chainflipSwapId?: number | string } // a result containing all routes that were successfully generated, or an error in the case where diff --git a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useChainflipStreamingProgress.tsx b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useChainflipStreamingProgress.tsx index b1a8ad05a9e..5c1fba25e1a 100644 --- a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useChainflipStreamingProgress.tsx +++ b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useChainflipStreamingProgress.tsx @@ -24,9 +24,9 @@ const DEFAULT_STREAMING_SWAP_METADATA: StreamingSwapMetadata = { } const getChainflipStreamingSwap = async ( - swapId: number | undefined, + swapId: number | string | undefined, ): Promise => { - if (!swapId) return + if (swapId == null) return const config = getConfig() const brokerUrl = config.VITE_CHAINFLIP_API_URL @@ -105,7 +105,7 @@ export const useChainflipStreamingProgress = ({ useQuery({ queryKey: ['streamingSwapData', chainflipSwapId, SwapperName.Chainflip], queryFn: - chainflipSwapId && + chainflipSwapId != null && swap && swap.swapperName === SwapperName.Chainflip && sellTxHash && diff --git a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx index 3160cfc2d69..2cc5e8d96dd 100644 --- a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx +++ b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx @@ -261,7 +261,7 @@ export const useTradeExecution = ( ) execution.on( TradeExecutionEvent.Status, - ({ buyTxHash, message, actualBuyAmountCryptoBaseUnit }) => { + ({ buyTxHash, message, actualBuyAmountCryptoBaseUnit, chainflipSwapId }) => { dispatch( tradeQuoteSlice.actions.setSwapTxMessage({ hopIndex, @@ -280,9 +280,9 @@ export const useTradeExecution = ( ) } - // Update the swap with the actual buy amount if available + // Update the swap with the actual buy amount and/or real Chainflip swap ID if available // Read fresh state to avoid stale closure - swapsById captured at render time may have outdated status - if (actualBuyAmountCryptoBaseUnit) { + if (actualBuyAmountCryptoBaseUnit || chainflipSwapId) { const freshActiveSwapId = swapSlice.selectors.selectActiveSwapId(store.getState()) if (freshActiveSwapId) { const currentSwap = swapSlice.selectors.selectSwapsById(store.getState())[ @@ -292,7 +292,10 @@ export const useTradeExecution = ( dispatch( swapSlice.actions.upsertSwap({ ...currentSwap, - actualBuyAmountCryptoBaseUnit, + ...(actualBuyAmountCryptoBaseUnit && { actualBuyAmountCryptoBaseUnit }), + ...(chainflipSwapId && { + metadata: { ...currentSwap.metadata, chainflipSwapId }, + }), }), ) } diff --git a/src/hooks/useActionCenterSubscribers/useSwapActionSubscriber.tsx b/src/hooks/useActionCenterSubscribers/useSwapActionSubscriber.tsx index 369283f643b..1f16c35251a 100644 --- a/src/hooks/useActionCenterSubscribers/useSwapActionSubscriber.tsx +++ b/src/hooks/useActionCenterSubscribers/useSwapActionSubscriber.tsx @@ -213,7 +213,7 @@ export const useSwapActionSubscriber = () => { if (!swap.sellTxHash) return if (!swap.receiveAddress) return - const { status, message, buyTxHash, actualBuyAmountCryptoBaseUnit } = + const { status, message, buyTxHash, actualBuyAmountCryptoBaseUnit, chainflipSwapId } = await queryClient.fetchQuery({ queryKey: tradeStatusQueryKey(swap.id, swap.sellTxHash), queryFn: () => @@ -252,7 +252,7 @@ export const useSwapActionSubscriber = () => { defaultExplorerBaseUrl, maybeSafeTx, stepSource: status && status !== TxStatus.Unknown ? swap.source : undefined, - maybeChainflipSwapId: `${swap.metadata.chainflipSwapId}`, + maybeChainflipSwapId: chainflipSwapId?.toString(), maybeNearIntentsDepositAddress: swap.metadata.nearIntentsSpecific?.depositAddress, ...(swap.swapperName === SwapperName.CowSwap ? { tradeId: txHash } : { txId: txHash }), ...(swap.metadata.relayerTxHash && { @@ -272,6 +272,9 @@ export const useSwapActionSubscriber = () => { buyTxHash, txLink, actualBuyAmountCryptoBaseUnit, + ...(chainflipSwapId && { + metadata: { ...swap.metadata, chainflipSwapId }, + }), }), ) diff --git a/src/lib/tradeExecution.ts b/src/lib/tradeExecution.ts index 77a9b064d73..fc4ebda605e 100644 --- a/src/lib/tradeExecution.ts +++ b/src/lib/tradeExecution.ts @@ -88,6 +88,7 @@ export const fetchTradeStatus = async ({ relayerTxHash, relayerExplorerTxLink, actualBuyAmountCryptoBaseUnit, + chainflipSwapId, } = await swapper.checkTradeStatus({ txHash: sellTxHash, chainId: sellAssetChainId, @@ -114,6 +115,7 @@ export const fetchTradeStatus = async ({ relayerTxHash, relayerExplorerTxLink, actualBuyAmountCryptoBaseUnit, + chainflipSwapId, } }