From 375520c429ca400cafebbd177b4aeee987f2de70 Mon Sep 17 00:00:00 2001 From: Louis Liu Date: Wed, 29 Jul 2026 16:42:32 +0800 Subject: [PATCH] feat: add Soroban contract ID validator --- src/lib/stellar-address.ts | 15 ++++++++++++++ tests/unit/stellar-address.test.ts | 33 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/lib/stellar-address.ts create mode 100644 tests/unit/stellar-address.test.ts diff --git a/src/lib/stellar-address.ts b/src/lib/stellar-address.ts new file mode 100644 index 0000000..4365577 --- /dev/null +++ b/src/lib/stellar-address.ts @@ -0,0 +1,15 @@ +import { StrKey } from "stellar-sdk"; + +export function isValidStellarPublicKey(value: unknown): value is string { + return typeof value === "string" && StrKey.isValidEd25519PublicKey(value); +} + +export function isValidSorobanContractId(value: unknown): value is string { + if (typeof value !== "string") return false; + try { + StrKey.decodeContract(value); + return true; + } catch { + return false; + } +} diff --git a/tests/unit/stellar-address.test.ts b/tests/unit/stellar-address.test.ts new file mode 100644 index 0000000..483d0c3 --- /dev/null +++ b/tests/unit/stellar-address.test.ts @@ -0,0 +1,33 @@ +import { Keypair, StrKey } from "stellar-sdk"; +import { + isValidSorobanContractId, + isValidStellarPublicKey, +} from "../../src/lib/stellar-address"; + +describe("stellar address validators", () => { + it("accepts a valid Soroban contract C-address", () => { + const contractId = StrKey.encodeContract(Buffer.alloc(32, 7)); + + expect(isValidSorobanContractId(contractId)).toBe(true); + }); + + it("rejects Stellar public keys when a Soroban contract ID is required", () => { + expect(isValidSorobanContractId(Keypair.random().publicKey())).toBe(false); + }); + + it("rejects malformed or non-string contract IDs", () => { + expect(isValidSorobanContractId("CNOT-A-VALID-CONTRACT")).toBe(false); + expect(isValidSorobanContractId("")).toBe(false); + expect(isValidSorobanContractId(null)).toBe(false); + expect(isValidSorobanContractId(undefined)).toBe(false); + expect(isValidSorobanContractId(123)).toBe(false); + }); + + it("keeps Stellar public key validation separate from contract IDs", () => { + const publicKey = Keypair.random().publicKey(); + const contractId = StrKey.encodeContract(Buffer.alloc(32, 8)); + + expect(isValidStellarPublicKey(publicKey)).toBe(true); + expect(isValidStellarPublicKey(contractId)).toBe(false); + }); +});