Skip to content
Open
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
26 changes: 22 additions & 4 deletions src/abi/encode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ const U8_MAX = 255;
const U16_MAX = 65_535;
const U32_MAX = 4_294_967_295;

/** Decimal integer pattern: optional minus, then digits (no leading zeros except bare "0"). */
const DECIMAL_INT_RE = /^-?(0|[1-9]\d*)$/;

/**
* Convert a string to BigInt with format validation.
* Rejects whitespace, scientific notation, decimal points, hex prefixes, and
* other inputs that would cause BigInt() to throw an unhelpful SyntaxError.
*/
function safeBigInt(val: string, caller: string): bigint {
if (!DECIMAL_INT_RE.test(val)) {
throw new Error(
`${caller}: string must be a decimal integer (got ${JSON.stringify(val)}). ` +
`Example valid inputs: "0", "123", "-456"`,
);
}
return BigInt(val);
}

/**
* Encode u8 (1 byte)
*/
Expand Down Expand Up @@ -43,7 +61,7 @@ export function encU32(val: number): Uint8Array {
* Input: bigint or string (decimal)
*/
export function encU64(val: bigint | string): Uint8Array {
const n = typeof val === "string" ? BigInt(val) : val;
const n = typeof val === "string" ? safeBigInt(val, "encU64") : val;
if (n < 0n) throw new Error("encU64: value must be non-negative");
if (n > 0xffff_ffff_ffff_ffffn) throw new Error("encU64: value exceeds u64 max");
const buf = new Uint8Array(8);
Expand All @@ -56,7 +74,7 @@ export function encU64(val: bigint | string): Uint8Array {
* Input: bigint or string (decimal, may be negative)
*/
export function encI64(val: bigint | string): Uint8Array {
const n = typeof val === "string" ? BigInt(val) : val;
const n = typeof val === "string" ? safeBigInt(val, "encI64") : val;
const min = -(1n << 63n);
const max = (1n << 63n) - 1n;
if (n < min || n > max) throw new Error("encI64: value out of range");
Expand All @@ -70,7 +88,7 @@ export function encI64(val: bigint | string): Uint8Array {
* Input: bigint or string (decimal)
*/
export function encU128(val: bigint | string): Uint8Array {
const n = typeof val === "string" ? BigInt(val) : val;
const n = typeof val === "string" ? safeBigInt(val, "encU128") : val;
if (n < 0n) throw new Error("encU128: value must be non-negative");
const max = (1n << 128n) - 1n;
if (n > max) throw new Error("encU128: value exceeds u128 max");
Expand All @@ -88,7 +106,7 @@ export function encU128(val: bigint | string): Uint8Array {
* Input: bigint or string (decimal, may be negative)
*/
export function encI128(val: bigint | string): Uint8Array {
const n = typeof val === "string" ? BigInt(val) : val;
const n = typeof val === "string" ? safeBigInt(val, "encI128") : val;
const min = -(1n << 127n);
const max = (1n << 127n) - 1n;
if (n < min || n > max) throw new Error("encI128: value out of range");
Expand Down
14 changes: 13 additions & 1 deletion src/abi/instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,19 @@ export interface PushOraclePriceArgs {
* @throws Error if price is 0 or exceeds MAX_ORACLE_PRICE
*/
export function encodePushOraclePrice(args: PushOraclePriceArgs): Uint8Array {
const price = typeof args.priceE6 === "string" ? BigInt(args.priceE6) : args.priceE6;
// Validate string format before BigInt conversion — rejects whitespace,
// scientific notation, decimal points, and hex prefixes that would cause
// an unhelpful SyntaxError from BigInt().
const price = typeof args.priceE6 === "string"
? (() => {
if (!/^-?(0|[1-9]\d*)$/.test(args.priceE6)) {
throw new Error(
`encodePushOraclePrice: priceE6 must be a decimal integer string (got ${JSON.stringify(args.priceE6)})`,
);
}
return BigInt(args.priceE6);
})()
: args.priceE6;

if (price === 0n) {
throw new Error("encodePushOraclePrice: price cannot be zero (division by zero in engine)");
Expand Down