Canonical encoding for BigInt (<bigint: Xn>) and Uint8Array (hex or <Uint8Array: 0x...>) used in the relay protocol. Provides both serialization (sourceOutputToRelay, transactionToHex) and deserialization (parseExtendedJson, toUint8Array, toBigInt) so dapps and wallets don't have to implement this independently.
92 lines
3 KiB
TypeScript
92 lines
3 KiB
TypeScript
// 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);
|
|
}
|