From 8c6e54218f6a1752448b73a2b1777919d1edd563 Mon Sep 17 00:00:00 2001 From: King Akinwale Date: Mon, 29 Jun 2026 08:14:47 +0000 Subject: [PATCH] feat(stellar-utils): add transaction hash validation utility validateTransactionHash ensures a 64-character lowercase hex string format for Stellar transaction hashes. --- shared/stellar-utils/index.ts | 7 ++++ shared/stellar-utils/package.json | 3 +- shared/stellar-utils/transaction-hash.test.ts | 32 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 shared/stellar-utils/transaction-hash.test.ts diff --git a/shared/stellar-utils/index.ts b/shared/stellar-utils/index.ts index f60785c..8f0824b 100644 --- a/shared/stellar-utils/index.ts +++ b/shared/stellar-utils/index.ts @@ -34,6 +34,13 @@ export function formatAmount(amount: string, decimals: number = 7): string { } } +/** + * Validate a Stellar transaction hash (64-character lowercase hex string) + */ +export function validateTransactionHash(hash: string): boolean { + return /^[0-9a-f]{64}$/.test(hash); +} + export function buildPaymentOperation(params: { source?: string; destination: string; asset: string; amount: string }){ // Placeholder: return normalized operation object return { diff --git a/shared/stellar-utils/package.json b/shared/stellar-utils/package.json index ecc4348..3da4969 100644 --- a/shared/stellar-utils/package.json +++ b/shared/stellar-utils/package.json @@ -11,6 +11,7 @@ }, "scripts": { "build": "tsc", - "type-check": "tsc --noEmit" + "type-check": "tsc --noEmit", + "test": "cross-env TS_NODE_TRANSPILE_ONLY=true node --loader ts-node/esm transaction-hash.test.ts" } } diff --git a/shared/stellar-utils/transaction-hash.test.ts b/shared/stellar-utils/transaction-hash.test.ts new file mode 100644 index 0000000..a8b47a6 --- /dev/null +++ b/shared/stellar-utils/transaction-hash.test.ts @@ -0,0 +1,32 @@ +import test from 'node:test'; +import assert from 'node:assert'; +import { validateTransactionHash } from './index.js'; + +test('validateTransactionHash returns true for a valid 64-char lowercase hex', () => { + const hash = 'abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789'; + assert.strictEqual(hash.length, 64); + assert.strictEqual(validateTransactionHash(hash), true); +}); + +test('validateTransactionHash returns false for hash with invalid length', () => { + assert.strictEqual(validateTransactionHash('a1b2c3d4'), false); + assert.strictEqual(validateTransactionHash(''), false); + assert.strictEqual(validateTransactionHash('a'.repeat(63)), false); + assert.strictEqual(validateTransactionHash('a'.repeat(65)), false); +}); + +test('validateTransactionHash returns false for uppercase hex', () => { + const hash = 'A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1B'; + assert.strictEqual(validateTransactionHash(hash), false); +}); + +test('validateTransactionHash returns false for non-hex characters', () => { + assert.strictEqual(validateTransactionHash('x'.repeat(64)), false); + assert.strictEqual(validateTransactionHash('g'.repeat(64)), false); + assert.strictEqual(validateTransactionHash('z1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b'), false); +}); + +test('validateTransactionHash returns false for non-string input', () => { + assert.strictEqual(validateTransactionHash(undefined as any), false); + assert.strictEqual(validateTransactionHash(null as any), false); +});