diff --git a/src/types/rpc.ts b/src/types/rpc.ts index 984aa65..83768ec 100644 --- a/src/types/rpc.ts +++ b/src/types/rpc.ts @@ -1,8 +1,13 @@ // Basic structure of the JSON-RPC response -export interface JSONRPCResponse { +export interface JSONRPCResponse { jsonrpc: string; - result: Result; - id: string; + id: number | string | null; + result?: T; + error?: { + code: number; + message: string; + data?: unknown; + }; } // Result contains various fields like final execution status, an array of receipts, etc. diff --git a/src/utils/signature.ts b/src/utils/signature.ts index b638843..cc3b3fc 100644 --- a/src/utils/signature.ts +++ b/src/utils/signature.ts @@ -30,27 +30,20 @@ export async function signatureFromTxHash( }, body: JSON.stringify(payload), }); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const json: JSONRPCResponse = await response.json(); - const jsonResponse = (await response.json()) as JSONRPCResponse; - let base64Sig = jsonResponse.result.status?.SuccessValue; - // TODO: Find an example when successValue isn't available and we need to enter this block. - if (base64Sig === "") { - // Extract receipts_outcome - const receiptsOutcome = jsonResponse.result.receipts_outcome; - // Map to get SuccessValue - const successValues = receiptsOutcome.map( - // eslint-disable-next-line - (outcome: any) => outcome.outcome.status.SuccessValue - ); - // Find the first non-empty value - base64Sig = successValues.find((value) => value && value.trim().length > 0); + if (json.error) { + throw new Error(`JSON-RPC error: ${json.error.message}`); } - if (base64Sig) { - const decodedValue = Buffer.from(base64Sig, "base64").toString("utf-8"); - const signature: MPCSignature = JSON.parse(decodedValue); - return transformSignature(signature); + + if (json.result) { + return signatureFromOutcome(json.result); } else { - throw new Error(`No valid values found in transaction receipt ${txHash}`); + throw new Error(`No FinalExecutionOutcome in response: ${json}`); } } @@ -70,8 +63,13 @@ export function signatureFromOutcome( outcome: FinalExecutionOutcome | Partial ): Signature { // TODO: Find example outcome when status is not of this casted type. - const b64Sig = (outcome.status as FinalExecutionStatus).SuccessValue!; - const decodedValue = Buffer.from(b64Sig, "base64").toString("utf-8"); - const signature = JSON.parse(decodedValue); - return transformSignature(signature); + const b64Sig = (outcome.status as FinalExecutionStatus).SuccessValue; + if (b64Sig) { + const decodedValue = Buffer.from(b64Sig, "base64").toString("utf-8"); + const signature = JSON.parse(decodedValue); + return transformSignature(signature); + } + throw new Error( + `No detectable signature found in transaction ${outcome.transaction_outcome?.id}` + ); } diff --git a/tests/unit/utils.signature.test.ts b/tests/unit/utils.signature.test.ts index a9d8fb8..843dc14 100644 --- a/tests/unit/utils.signature.test.ts +++ b/tests/unit/utils.signature.test.ts @@ -18,22 +18,15 @@ describe("utility: get Signature", () => { }); }); - // Still need a NEW example of this! - // it("successful: alternative signatureFromTxHash", async () => { - // const sig = await signatureFromTxHash( - // url, - // "EK4XUwyR29w6eaSfSSPb8he3y7nkTQSbYJVXgSx5vZ4T" - // ); - // expect(sig).toEqual({ - // big_r: - // "024598E193A9377B98A5B4621BA81FDEEA9DED3E3E7F41C073D0537BC2769C10FC", - // big_s: "65D23B4EA333FFC5486FA295B7AEAB02EACA4E35E22B55108563A63199B96558", - // }); - // }); - - it("failed: signatureFromTxHash", async () => { + it("signatureFromTxHash fails with no signature", async () => { await expect(signatureFromTxHash(url, failedHash)).rejects.toThrow( - `No valid values found in transaction receipt ${failedHash}` + `No detectable signature found in transaction ${failedHash}` + ); + }); + + it("signatureFromTxHash fails with parse error", async () => { + await expect(signatureFromTxHash(url, "nonsense")).rejects.toThrow( + "JSON-RPC error: Parse error" ); });