Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/shiny-ants-say.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ckb-ccc/core": patch
---

feat: `hexFrom` passes through normalized hex; `numToHex` now throws on negative values
34 changes: 32 additions & 2 deletions packages/core/src/hex/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,33 @@ export type Hex = `0x${string}`;
export type HexLike = BytesLike;

/**
* Converts a HexLike value to a Hex string.
* @public
* Determines whether a given value is a properly formatted hexadecimal string (ccc.Hex).
*
* A valid hexadecimal string:
* - Has at least two characters.
Comment thread
Hanssen0 marked this conversation as resolved.
* - Starts with "0x".
* - Has an even length.
* - Contains only characters representing digits (0-9) or lowercase letters (a-f) after the "0x" prefix.
*
* @param v - The value to validate as a hexadecimal (ccc.Hex) string.
* @returns True if the string is a valid hex string, false otherwise.
*/
function isNormalizedHex(v: unknown): v is Hex {
if (!(typeof v === "string" && v.length % 2 === 0 && v.startsWith("0x"))) {
return false;
}

for (let i = 2; i < v.length; i++) {
const c = v.charAt(i);
if (!(("0" <= c && c <= "9") || ("a" <= c && c <= "f"))) {
return false;
}
}
return true;
}

/**
* Returns the hexadecimal representation of the given value.
*
* @param hex - The value to convert, which can be a string, Uint8Array, ArrayBuffer, or number array.
* @returns A Hex string representing the value.
Expand All @@ -26,5 +51,10 @@ export type HexLike = BytesLike;
* ```
*/
export function hexFrom(hex: HexLike): Hex {
// Pass through already-normalized hex to avoid allocating a new string.
if (isNormalizedHex(hex)) {
return hex;
}

return `0x${bytesTo(bytesFrom(hex), "hex")}`;
}
21 changes: 17 additions & 4 deletions packages/core/src/num/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Bytes, BytesLike, bytesConcat, bytesFrom } from "../bytes/index.js";
import { Zero } from "../fixedPoint/index.js";
import { Hex, HexLike, hexFrom } from "../hex/index.js";

/**
Expand Down Expand Up @@ -90,19 +91,31 @@ export function numFrom(val: NumLike): Num {
}

/**
* Converts a NumLike value to a hexadecimal string.
* Converts a {@link NumLike} value into its hexadecimal string representation, prefixed with `0x`.
*
* @remarks
* This function returns the direct hexadecimal representation of the number, which may have an odd number of digits.
* For a full-byte representation (an even number of hex digits), consider using {@link numToBytes}, {@link numLeToBytes}, or {@link numBeToBytes} and then converting the resulting byte array to a hex string.
Comment thread
Hanssen0 marked this conversation as resolved.
*
* @public
*
* @param val - The value to convert, which can be a string, number, bigint, or HexLike.
* @returns A Hex string representing the numeric value.
* @returns A Hex string representing the number.
*
* @throws {Error} If the normalized numeric value is negative.
*
* @example
* ```typescript
* const hex = numToHex(12345); // Outputs "0x3039"
* const hex = numToHex(4660); // "0x1234"
* const oddLengthHex = numToHex(10); // "0xa"
* ```
*/
export function numToHex(val: NumLike): Hex {
return `0x${numFrom(val).toString(16)}`;
const v = numFrom(val);
if (v < Zero) {
throw new Error("value must be non-negative");
}
return `0x${v.toString(16)}`;
}

/**
Expand Down