diff --git a/packages/passport/sdk/src/zkEvm/relayerClient.test.ts b/packages/passport/sdk/src/zkEvm/relayerClient.test.ts index 780cc82dc8..4a40f79a2d 100644 --- a/packages/passport/sdk/src/zkEvm/relayerClient.test.ts +++ b/packages/passport/sdk/src/zkEvm/relayerClient.test.ts @@ -44,6 +44,8 @@ describe('relayerClient', () => { const data = '0x123'; (global.fetch as jest.Mock).mockResolvedValue({ + ok: true, + text: () => Promise.resolve(JSON.stringify({ result: transactionHash })), json: () => ({ result: transactionHash, }), @@ -69,6 +71,36 @@ describe('relayerClient', () => { }], }); }); + + it('throws HTTP error for non-ok response', async () => { + const to = '0xd64b0d2d72bb1b3f18046b8a7fc6c9ee6bccd287'; + const data = '0x123'; + + (global.fetch as jest.Mock).mockResolvedValue({ + ok: false, + status: 401, + statusText: 'Unauthorized', + text: () => Promise.resolve('{"error":"invalid_token"}'), + }); + + await expect(relayerClient.ethSendTransaction(to, data)).rejects.toThrow( + 'Relayer HTTP error: 401. Content: "{"error":"invalid_token"}"', + ); + }); + + it('throws JSON parse error for invalid response', async () => { + const to = '0xd64b0d2d72bb1b3f18046b8a7fc6c9ee6bccd287'; + const data = '0x123'; + + (global.fetch as jest.Mock).mockResolvedValue({ + ok: true, + text: () => Promise.resolve('invalid json'), + }); + + await expect(relayerClient.ethSendTransaction(to, data)).rejects.toThrow( + 'Relayer JSON parse error: Unexpected token \'i\', "invalid json" is not valid JSON. Content: "invalid json"', + ); + }); }); describe('imGetTransactionByHash', () => { @@ -82,6 +114,8 @@ describe('relayerClient', () => { }; (global.fetch as jest.Mock).mockResolvedValue({ + ok: true, + text: () => Promise.resolve(JSON.stringify({ result: relayerTransaction })), json: () => ({ result: relayerTransaction, }), @@ -118,6 +152,8 @@ describe('relayerClient', () => { }]; (global.fetch as jest.Mock).mockResolvedValue({ + ok: true, + text: () => Promise.resolve(JSON.stringify({ result: feeOptions })), json: () => ({ result: feeOptions, }), @@ -152,6 +188,8 @@ describe('relayerClient', () => { const relayerSignature = '0x123'; (global.fetch as jest.Mock).mockResolvedValue({ + ok: true, + text: () => Promise.resolve(JSON.stringify({ result: relayerSignature })), json: () => ({ result: relayerSignature, }), @@ -186,6 +224,8 @@ describe('relayerClient', () => { const relayerSignature = '0x123'; (global.fetch as jest.Mock).mockResolvedValue({ + ok: true, + text: () => Promise.resolve(JSON.stringify({ result: relayerSignature })), json: () => ({ result: relayerSignature, }), diff --git a/packages/passport/sdk/src/zkEvm/relayerClient.ts b/packages/passport/sdk/src/zkEvm/relayerClient.ts index ddbe226d66..1eaae5a077 100644 --- a/packages/passport/sdk/src/zkEvm/relayerClient.ts +++ b/packages/passport/sdk/src/zkEvm/relayerClient.ts @@ -102,6 +102,12 @@ export class RelayerClient { this.authManager = authManager; } + private static getResponsePreview(text: string): string { + return text.length > 100 + ? `${text.substring(0, 50)}...${text.substring(text.length - 50)}` + : text; + } + private async postToRelayer(request: RelayerTransactionRequest): Promise { const body: RelayerTransactionRequest & JsonRpc = { id: 1, @@ -120,9 +126,19 @@ export class RelayerClient { body: JSON.stringify(body), }); - const jsonResponse = await response.json(); - if (jsonResponse.error) { - throw jsonResponse.error; + if (!response.ok) { + const responseText = await response.text(); + const preview = RelayerClient.getResponsePreview(responseText); + throw new Error(`Relayer HTTP error: ${response.status}. Content: "${preview}"`); + } + + const responseText = await response.text(); + let jsonResponse; + try { + jsonResponse = JSON.parse(responseText); + } catch (parseError) { + const preview = RelayerClient.getResponsePreview(responseText); + throw new Error(`Relayer JSON parse error: ${parseError instanceof Error ? parseError.message : 'Unknown error'}. Content: "${preview}"`); } return jsonResponse;