WizardConnect/packages/core/src/serialize.ts

93 lines
3 KiB
TypeScript
Raw Normal View History

// Copyright (C) 2026 Whiterun LLC,
// This software is licensed under the GNU Lesser General Public License (LGPL), version 3.0 or later.
// A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/lgpl-3.0.html
/**
* Serialization helpers for converting between native libauth types and the
* relay-safe JSON format used by the WizardConnect protocol.
*
* The relay transmits messages as JSON, which cannot represent BigInt or
* Uint8Array natively. This module provides the canonical encoding:
*
* - Uint8Array hex string (or `<Uint8Array: 0x...>` extended format)
* - BigInt `<bigint: Xn>` string
*
* Both dapps and wallets should use these helpers to ensure interoperability.
*/
import { hexToBin } from "@bitauth/libauth";
// ---------------------------------------------------------------------------
// Deserialization (relay JSON → native types)
// ---------------------------------------------------------------------------
const BIGINT_RE = /^<bigint: (?<bigint>[0-9]*)n>$/;
const UINT8_RE = /^<Uint8Array: 0x(?<hex>[0-9a-f]*)>$/u;
/**
* Parse a full JSON string that may contain extended-format values.
* Handles both `<Uint8Array: 0x...>` and `<bigint: ...n>` formats.
*/
export function parseExtendedJson(jsonString: string): any {
return JSON.parse(jsonString, (_key, value) => {
if (typeof value === "string") {
const bigintMatch = value.match(BIGINT_RE);
if (bigintMatch) return BigInt(bigintMatch[1]);
const uint8Match = value.match(UINT8_RE);
if (uint8Match) return hexToBin(uint8Match[1]);
}
return value;
});
}
/**
* Check if a string contains extended JSON markers.
*/
export function isExtendedJsonFormat(str: string): boolean {
return UINT8_RE.test(str) || BIGINT_RE.test(str);
}
/**
* Parse a single extended JSON value string to its native type.
* Returns the original string if it doesn't match any known format.
*/
export function parseExtendedJsonValue(
value: string,
): Uint8Array | bigint | string {
const bigintMatch = value.match(BIGINT_RE);
if (bigintMatch) return BigInt(bigintMatch[1]);
const uint8Match = value.match(UINT8_RE);
if (uint8Match) return hexToBin(uint8Match[1]);
return value;
}
/**
* Convert a value to Uint8Array. Accepts:
* - Uint8Array (returned as-is)
* - hex string
* - extended JSON format string (`<Uint8Array: 0x...>`)
*/
export function toUint8Array(value: string | Uint8Array): Uint8Array {
if (value instanceof Uint8Array) return value;
if (isExtendedJsonFormat(value))
return parseExtendedJsonValue(value) as Uint8Array;
return hexToBin(value);
}
/**
* Convert a value to bigint. Accepts:
* - bigint (returned as-is)
* - number
* - numeric string
* - extended JSON format string (`<bigint: Xn>`)
*/
export function toBigInt(value: string | number | bigint): bigint {
if (typeof value === "bigint") return value;
if (typeof value === "string") {
if (isExtendedJsonFormat(value))
return parseExtendedJsonValue(value) as bigint;
return BigInt(value);
}
return BigInt(value);
}