Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,50 @@ describe("Sushi Action Provider", () => {
expect(result).toContain(`Swapped`);
});

it("should report an executed swap when the Route event is missing from the receipt", async () => {
const args: Parameters<(typeof actionProvider)["swap"]>[1] = {
amount: formatUnits(amountIn, tokenIn.decimals),
fromAssetAddress: tokenIn.address,
toAssetAddress: tokenOut.address,
maxSlippage: 0.005,
};

/*
* 1. Mock the readContract which checks the decimals of the fromAssetAddress token (18, default)
* 2. Mock the readContract which checks for the balance of the fromAssetAddress token (1000000, enough balance)
* 3. Mock the readContract which checks for the approval (1000000, approved)
*/
mockWallet.readContract
.mockResolvedValueOnce(tokenIn.decimals)
.mockResolvedValueOnce(amountIn)
.mockResolvedValueOnce(amountIn);

mockWallet.sendTransaction.mockResolvedValue(txHash);

// Swap tx succeeds on-chain but the receipt contains no Route event log
mockWallet.waitForTransactionReceipt.mockResolvedValueOnce({
status: "success",
logs: [],
});

mockedGetSwap.mockReturnValue(
getSuccessfullSwapResponse({
tokenIn,
amountIn,
tokenOut,
amountOut,
}),
);

const result = await actionProvider.swap(mockWallet, args);

expect(mockWallet.sendTransaction).toHaveBeenCalledTimes(1); // Swap only
expect(result).toContain("Swap executed");
expect(result).not.toContain("Error");
expect(result).toContain(`Transaction hash: ${txHash}`);
expect(result).toContain("Do not retry this swap automatically");
});

it("should fail if there's no route", async () => {
const args: Parameters<(typeof actionProvider)["swap"]>[1] = {
amount: formatUnits(amountIn, tokenIn.decimals),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ Important notes:
}

// Find the Route event log, which includes the actual amountOut
const [routeLog] = swapReceipt.logs
const [routeLog] = (swapReceipt.logs ?? [])
.filter(
log =>
encodeEventTopics({
Expand All @@ -165,6 +165,18 @@ Important notes:
}),
);

if (!routeLog) {
// The swap succeeded on-chain, but the receipt has no Route event to
// decode amounts from (e.g. fills not routed through RouteProcessor9).
// Report this as an executed swap — returning a generic error here
// leads agents to retry and execute a second, unintended swap.
return `Swap executed on ${chain.shortName}, but the Route event was not found in the transaction receipt, so the exact output amount could not be decoded.
- Quoted AmountOut: ${formatUnits(BigInt(secondSwap.swap.assumedAmountOut), secondSwap.swap.tokenTo.decimals)} ${secondSwap.swap.tokenTo.symbol} (${args.toAssetAddress})
- Transaction hash: ${swapHash}
- Transaction link: ${chain.getTransactionUrl(swapHash)}
Do not retry this swap automatically; check the transaction first.`;
}

return `Swapped ${formatUnits(routeLog.args.amountIn, secondSwap.swap.tokenFrom.decimals)} of ${secondSwap.swap.tokenFrom.symbol} (${args.fromAssetAddress}) for ${formatUnits(routeLog.args.amountOut, secondSwap.swap.tokenTo.decimals)} of ${secondSwap.swap.tokenTo.symbol} (${args.toAssetAddress}) on ${chain.shortName}
- Transaction hash: ${swapHash}
- Transaction link: ${chain.getTransactionUrl(swapHash)}`;
Expand Down
Loading