From 19efa7a166b3a98299a895481a39e7356db26184 Mon Sep 17 00:00:00 2001 From: Benjamin Smith Date: Tue, 12 Nov 2024 20:56:46 +0100 Subject: [PATCH] Transaction Serializable & RlpHex Guards --- src/types/guards.ts | 30 +++++++++++++- tests/unit/types.guards.test.ts | 70 ++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/types/guards.ts b/src/types/guards.ts index d59cf5e..1275ce0 100644 --- a/src/types/guards.ts +++ b/src/types/guards.ts @@ -1,4 +1,11 @@ -import { isAddress, TypedDataDomain } from "viem"; +import { + Hex, + isAddress, + parseTransaction, + serializeTransaction, + TransactionSerializable, + TypedDataDomain, +} from "viem"; import { EIP712TypedData, SignMethod, TypedMessageTypes } from "."; export function isSignMethod(method: unknown): method is SignMethod { @@ -76,3 +83,24 @@ export const isEIP712TypedData = (obj: unknown): obj is EIP712TypedData => { typeof candidate.primaryType === "string" ); }; + +// Cheeky attempt to serialize. return true if successful! +export function isTransactionSerializable( + data: unknown +): data is TransactionSerializable { + try { + serializeTransaction(data as TransactionSerializable); + return true; + } catch (error) { + return false; + } +} + +export function isRlpHex(data: unknown): data is Hex { + try { + parseTransaction(data as Hex); + return true; + } catch (error) { + return false; + } +} diff --git a/tests/unit/types.guards.test.ts b/tests/unit/types.guards.test.ts index 83bf562..d334331 100644 --- a/tests/unit/types.guards.test.ts +++ b/tests/unit/types.guards.test.ts @@ -1,4 +1,17 @@ -import { isEIP712TypedData, isSignMethod } from "../../src/"; +import { TransactionSerializable } from "viem"; +import { + isEIP712TypedData, + isRlpHex, + isSignMethod, + isTransactionSerializable, +} from "../../src/"; + +const validEIP1559Transaction: TransactionSerializable = { + to: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + value: BigInt(1000000000000000000), // 1 ETH + chainId: 1, + maxFeePerGas: 1n, +}; describe("Type Guards", () => { it("isSignMethod", async () => { expect(isSignMethod("poop")).toBe(false); @@ -52,3 +65,58 @@ describe("Type Guards", () => { expect(isEIP712TypedData(typedData)).toBe(true); }); }); + +describe("isTransactionSerializable", () => { + it("should return true for valid transaction data", () => { + expect(isTransactionSerializable(validEIP1559Transaction)).toBe(true); + }); + + it("should return false for invalid transaction data", () => { + const invalidCases = [ + null, + undefined, + {}, + { to: "invalid-address" }, + { value: "not-a-bigint" }, + { chainId: "not-a-number" }, + "random string", + 123, + [], + ]; + + invalidCases.forEach((testCase) => { + expect(isTransactionSerializable(testCase)).toBe(false); + }); + }); +}); + +describe("isRlpHex", () => { + it("should return true for valid RLP-encoded transaction hex", () => { + // This is an example of a valid RLP-encoded transaction hex: + + // serializeTransaction(validEIP1559Transaction) + const validRlpHex = + "0x02e501808001809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0"; + expect(isRlpHex(validRlpHex)).toBe(true); + }); + + it("should return false for invalid RLP hex data", () => { + const invalidCases = [ + null, + undefined, + {}, + "not-a-hex", + "0x", // empty hex + "0x1234", // too short + "0xinvalid", + 123, + [], + // Invalid RLP structure but valid hex + "0x1234567890abcdef", + ]; + + invalidCases.forEach((testCase) => { + expect(isRlpHex(testCase)).toBe(false); + }); + }); +});