From a0b11b23cbb03d9c2636bfbff3bc23c86961a710 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 17:34:41 +0000 Subject: [PATCH 1/2] Initial plan From 3cf212f69a542895c682eff142937562999a9818 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 17:38:43 +0000 Subject: [PATCH 2/2] Address PR review: fix nonces probe, add tx validation, add dedicated no-permit messages Agent-Logs-Url: https://github.com/pendulum-chain/vortex/sessions/bb4b3a39-0478-47f2-ab90-2834439c9c34 Co-authored-by: ebma <6690623+ebma@users.noreply.github.com> --- .../squidrouter-permit-execution-handler.ts | 9 ++++++++ .../offramp/routes/evm-to-alfredpay.ts | 21 ++++++++++++++++--- .../src/pages/progress/phaseMessages.ts | 14 ++++++++++--- apps/frontend/src/translations/en.json | 3 +++ apps/frontend/src/translations/pt.json | 3 +++ 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/apps/api/src/api/services/phases/handlers/squidrouter-permit-execution-handler.ts b/apps/api/src/api/services/phases/handlers/squidrouter-permit-execution-handler.ts index a2b38bdfc..8a12ad51e 100644 --- a/apps/api/src/api/services/phases/handlers/squidrouter-permit-execution-handler.ts +++ b/apps/api/src/api/services/phases/handlers/squidrouter-permit-execution-handler.ts @@ -128,6 +128,15 @@ export class SquidrouterPermitExecuteHandler extends BasePhaseHandler { if (!receipt || receipt.status !== "success") { throw this.createRecoverableError(`${label} tx failed: ${hash}`); } + + // Verify the transaction was sent by the expected wallet + const expectedFrom = (state.state.walletAddress as string)?.toLowerCase(); + if (expectedFrom && receipt.from.toLowerCase() !== expectedFrom) { + throw this.createUnrecoverableError( + `${label} tx from address mismatch: expected ${expectedFrom}, got ${receipt.from.toLowerCase()}` + ); + } + logger.info(`${label} tx confirmed: ${hash}`); } diff --git a/apps/api/src/api/services/transactions/offramp/routes/evm-to-alfredpay.ts b/apps/api/src/api/services/transactions/offramp/routes/evm-to-alfredpay.ts index b70080eef..ac7a6179c 100644 --- a/apps/api/src/api/services/transactions/offramp/routes/evm-to-alfredpay.ts +++ b/apps/api/src/api/services/transactions/offramp/routes/evm-to-alfredpay.ts @@ -22,7 +22,16 @@ import { UnsignedTx } from "@vortexfi/shared"; import Big from "big.js"; -import { encodeAbiParameters, encodeFunctionData, keccak256, PublicClient, pad, parseAbiParameters, toHex } from "viem"; +import { + ContractFunctionExecutionError, + encodeAbiParameters, + encodeFunctionData, + keccak256, + pad, + parseAbiParameters, + PublicClient, + toHex +} from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { MOONBEAM_EXECUTOR_PRIVATE_KEY } from "../../../../../constants/constants"; import AlfredPayCustomer from "../../../../../models/alfredPayCustomer.model"; @@ -240,8 +249,14 @@ export async function prepareEvmToAlfredpayOfframpTransactions({ args: [userAddress], functionName: "nonces" })) as bigint; - } catch { - userNonce = null; + } catch (error) { + // Only treat contract-level failures (function not found / execution reverted) as "no permit". + // Re-throw network/transport errors so the request can be retried. + if (error instanceof ContractFunctionExecutionError) { + userNonce = null; + } else { + throw error; + } } const supportsPermit = userNonce !== null; diff --git a/apps/frontend/src/pages/progress/phaseMessages.ts b/apps/frontend/src/pages/progress/phaseMessages.ts index 5449426fe..8988e261a 100644 --- a/apps/frontend/src/pages/progress/phaseMessages.ts +++ b/apps/frontend/src/pages/progress/phaseMessages.ts @@ -100,9 +100,17 @@ export function getMessageForPhase(ramp: RampState | undefined, t: TFunction<"tr assetSymbol: outputAssetSymbol }), squidRouterApprove: getSquidRouterSwapMessage(), - squidRouterNoPermitApprove: getSquidRouterPermitMessage(), - squidRouterNoPermitSwap: getSquidRouterPermitMessage(), - squidRouterNoPermitTransfer: getSquidRouterPermitMessage(), + squidRouterNoPermitApprove: t("pages.progress.squidRouterNoPermitApprove", { + assetSymbol: inputAssetSymbol, + fromNetwork: quote.from + }), + squidRouterNoPermitSwap: t("pages.progress.squidRouterNoPermitSwap", { + assetSymbol: inputAssetSymbol, + fromNetwork: quote.from + }), + squidRouterNoPermitTransfer: t("pages.progress.squidRouterNoPermitTransfer", { + assetSymbol: inputAssetSymbol + }), squidRouterPay: getSquidRouterSwapMessage(), squidRouterPermitExecute: getSquidRouterPermitMessage(), squidRouterSwap: getSquidRouterSwapMessage(), diff --git a/apps/frontend/src/translations/en.json b/apps/frontend/src/translations/en.json index 4b4117961..8f35b3d46 100644 --- a/apps/frontend/src/translations/en.json +++ b/apps/frontend/src/translations/en.json @@ -1185,6 +1185,9 @@ "pendulumToAssethubXcm": "Transferring {{assetSymbol}} from Pendulum --> AssetHub", "pendulumToHydrationXcm": "Transferring {{assetSymbol}} from Pendulum --> Hydration", "pendulumToMoonbeamXcm": "Transferring {{assetSymbol}} from Pendulum --> Moonbeam", + "squidRouterNoPermitApprove": "Approving {{assetSymbol}} for transfer from {{fromNetwork}}", + "squidRouterNoPermitSwap": "Transferring {{assetSymbol}} from {{fromNetwork}} via bridge", + "squidRouterNoPermitTransfer": "Transferring {{assetSymbol}} to processing account", "squidRouterPermitExecute": "Initializing the transfer of {{assetSymbol}} from {{fromNetwork}}", "squidRouterSwap": "Transferring {{assetSymbol}} from {{fromNetwork}} to {{toNetwork}}", "stellarPayment": "Transferring {{assetSymbol}} from Stellar --> local partner", diff --git a/apps/frontend/src/translations/pt.json b/apps/frontend/src/translations/pt.json index 19ed043d5..b77a9db43 100644 --- a/apps/frontend/src/translations/pt.json +++ b/apps/frontend/src/translations/pt.json @@ -1189,6 +1189,9 @@ "pendulumToAssethubXcm": "Transferindo {{assetSymbol}} de Pendulum --> AssetHub", "pendulumToHydrationXcm": "Transferindo {{assetSymbol}} de Pendulum --> Hydration", "pendulumToMoonbeamXcm": "Transferindo {{assetSymbol}} de Pendulum --> Moonbeam", + "squidRouterNoPermitApprove": "Aprovando {{assetSymbol}} para transferĂȘncia de {{fromNetwork}}", + "squidRouterNoPermitSwap": "Transferindo {{assetSymbol}} de {{fromNetwork}} via bridge", + "squidRouterNoPermitTransfer": "Transferindo {{assetSymbol}} para conta de processamento", "squidRouterPermitExecute": "Autorizando transferĂȘncia de {{assetSymbol}} de {{fromNetwork}}", "squidRouterSwap": "Transferindo {{assetSymbol}} de {{fromNetwork}} para {{toNetwork}}", "stellarPayment": "Transferindo {{assetSymbol}} de Stellar --> parceiro local",