diff --git a/lib/contract.ts b/lib/contract.ts index 200884d5..37f950c5 100644 --- a/lib/contract.ts +++ b/lib/contract.ts @@ -131,7 +131,10 @@ import { if (txResponse.status !== "SUCCESS") { console.error(`Transaction failed for ${functName} with status: ${txResponse.status}`, JSON.stringify(txResponse, null, 2)); - throw new Error(`Transaction failed with status: ${txResponse.status}`); + throw new Error( + `Transaction failed with status: ${txResponse.status} ` + + `(function: ${functName}, contract: ${targetContractId}, network: ${config.network})` + ); } // Parse return value if present (e.g., for withdraw) @@ -219,7 +222,10 @@ import { } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); console.error("Failed to swap:", errorMessage); - throw error; + throw new Error( + `Failed to swap (to=${to}, buyA=${buyA}, out=${out}, inMax=${inMax}, ` + + `network=${config?.network ?? "testnet"}): ${errorMessage}` + ); } } diff --git a/lib/dex.ts b/lib/dex.ts index d14da97a..ca0303ed 100644 --- a/lib/dex.ts +++ b/lib/dex.ts @@ -195,7 +195,16 @@ export async function quoteSwap( const response = await fetchImpl(buildPathEndpointUrl(client, params)); if (!response.ok) { - throw new Error(`Failed to fetch path quotes: ${response.status} ${response.statusText}`); + const sendAssetLabel = assetInputToHorizonAsset(params.sendAsset); + const destAssetLabel = assetInputToHorizonAsset(params.destAsset); + const amountLabel = params.mode === "strict-send" + ? `sendAmount=${params.sendAmount}` + : `destAmount=${params.destAmount}`; + throw new Error( + `Failed to fetch path quotes for ${params.mode} swap ` + + `(${sendAssetLabel} → ${destAssetLabel}, ${amountLabel}) ` + + `on ${client.network}: ${response.status} ${response.statusText}` + ); } const payload = (await response.json()) as HorizonPathResponse; @@ -223,7 +232,16 @@ export async function swapBestRoute( const bestQuote = quotes[0]; if (!bestQuote) { - throw new Error("No route available for the requested swap"); + const sendAssetLabel = assetInputToHorizonAsset(params.sendAsset); + const destAssetLabel = assetInputToHorizonAsset(params.destAsset); + const amountLabel = params.mode === "strict-send" + ? `sendAmount=${params.sendAmount}` + : `destAmount=${params.destAmount}`; + throw new Error( + `No route available for ${params.mode} swap ` + + `(${sendAssetLabel} → ${destAssetLabel}, ${amountLabel}) ` + + `on ${client.network}` + ); } const createServer = @@ -417,7 +435,8 @@ async function validateDestinationAssetSupport( if (!supportsAsset) { throw new Error( - "Destination account does not trust the requested destination asset" + `Destination account ${destination} does not trust asset ${destAsset.code}:${destAsset.issuer}. ` + + `The destination account must have a trustline for this asset before receiving it.` ); } } diff --git a/tests/integration/dex-workflow.test.ts b/tests/integration/dex-workflow.test.ts index 97fa6639..bd78104b 100644 --- a/tests/integration/dex-workflow.test.ts +++ b/tests/integration/dex-workflow.test.ts @@ -158,7 +158,7 @@ describe("dex workflow", () => { }, { fetchImpl } ) - ).rejects.toThrow("No route available"); + ).rejects.toThrow("No route available for"); }); it("surfaces destination trustline-style submission failures", async () => { diff --git a/tests/unit/lib/dex.test.ts b/tests/unit/lib/dex.test.ts index 4754b605..88afa2cc 100644 --- a/tests/unit/lib/dex.test.ts +++ b/tests/unit/lib/dex.test.ts @@ -368,7 +368,7 @@ describe("dex helpers", () => { }, { fetchImpl } ) - ).rejects.toThrow("Destination account does not trust the requested destination asset"); + ).rejects.toThrow("does not trust asset EUR:"); expect(fetchImpl).toHaveBeenCalledTimes(1); });