@wormhole-foundation/sdk-base
Advanced tools
Comparing version 0.1.8-beta.7 to 0.1.8-beta.8
@@ -1,4 +0,4 @@ | ||
export declare const protocols: readonly ["WormholeCore", "TokenBridge", "AutomaticTokenBridge", "CircleBridge", "AutomaticCircleBridge", "Relayer", "NftBridge"]; | ||
export declare const protocols: readonly ["WormholeCore", "TokenBridge", "AutomaticTokenBridge", "CircleBridge", "AutomaticCircleBridge", "Relayer", "IbcBridge", "NftBridge"]; | ||
export type ProtocolName = (typeof protocols)[number]; | ||
export declare const isProtocolName: (protocol: string) => protocol is "WormholeCore" | "TokenBridge" | "AutomaticTokenBridge" | "CircleBridge" | "AutomaticCircleBridge" | "Relayer" | "NftBridge"; | ||
export declare const isProtocolName: (protocol: string) => protocol is "WormholeCore" | "TokenBridge" | "AutomaticTokenBridge" | "CircleBridge" | "AutomaticCircleBridge" | "Relayer" | "IbcBridge" | "NftBridge"; | ||
//# sourceMappingURL=protocols.d.ts.map |
@@ -35,2 +35,3 @@ "use strict"; | ||
"Relayer", | ||
"IbcBridge", | ||
// not implemented | ||
@@ -37,0 +38,0 @@ "NftBridge", |
@@ -26,9 +26,12 @@ "use strict"; | ||
} | ||
function deserializeUint(encoded, offset, size) { | ||
let value = 0n; | ||
for (let i = 0; i < size; ++i) | ||
value += BigInt(encoded[offset + i]) << BigInt((size - 1 - i) * 8); | ||
function deserializeNum(encoded, offset, bytes, endianness = "big", signed = false) { | ||
let val = 0n; | ||
for (let i = 0; i < bytes; ++i) | ||
val |= BigInt(encoded[offset + i]) << BigInt(8 * (endianness === "big" ? bytes - i - 1 : i)); | ||
//check sign bit if value is indeed signed and adjust accordingly | ||
if (signed && (encoded[offset + (endianness === "big" ? 0 : bytes - 1)] & 0x80)) | ||
val -= 1n << BigInt(8 * bytes); | ||
return [ | ||
((size > layout_1.numberMaxSize) ? value : Number(value)), | ||
updateOffset(encoded, offset, size) | ||
((bytes > layout_1.numberMaxSize) ? val : Number(val)), | ||
updateOffset(encoded, offset, bytes) | ||
]; | ||
@@ -39,10 +42,11 @@ } | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": { | ||
const [value, newOffset] = deserializeUint(encoded, offset, item.size); | ||
if ((0, layout_1.isUintType)(item.custom)) { | ||
(0, utils_1.checkUintEquals)(item.custom, value); | ||
const [value, newOffset] = deserializeNum(encoded, offset, item.size, item.endianness, item.binary === "int"); | ||
if ((0, layout_1.isNumType)(item.custom)) { | ||
(0, utils_1.checkNumEquals)(item.custom, value); | ||
return [item.custom, newOffset]; | ||
} | ||
if ((0, layout_1.isUintType)(item?.custom?.from)) { | ||
(0, utils_1.checkUintEquals)(item.custom.from, value); | ||
if ((0, layout_1.isNumType)(item?.custom?.from)) { | ||
(0, utils_1.checkNumEquals)(item.custom.from, value); | ||
return [item.custom.to, newOffset]; | ||
@@ -75,3 +79,4 @@ } | ||
let length; | ||
[length, offset] = deserializeUint(encoded, offset, item.lengthSize); | ||
[length, offset] = | ||
deserializeNum(encoded, offset, item.lengthSize, item.lengthEndianness); | ||
newOffset = updateOffset(encoded, offset, length); | ||
@@ -101,3 +106,3 @@ } | ||
if (item.lengthSize !== undefined) { | ||
const [length, newOffset] = deserializeUint(encoded, offset, item.lengthSize); | ||
const [length, newOffset] = deserializeNum(encoded, offset, item.lengthSize, item.lengthEndianness); | ||
offset = newOffset; | ||
@@ -116,3 +121,3 @@ for (let i = 0; i < length; ++i) | ||
case "switch": { | ||
const [id, newOffset] = deserializeUint(encoded, offset, item.idSize); | ||
const [id, newOffset] = deserializeNum(encoded, offset, item.idSize, item.idEndianness); | ||
const { idLayoutPairs } = item; | ||
@@ -119,0 +124,0 @@ if (idLayoutPairs.length === 0) |
@@ -29,6 +29,7 @@ "use strict"; | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": { | ||
const fixedVal = (0, layout_1.isUintType)(item.custom) | ||
const fixedVal = (0, layout_1.isNumType)(item.custom) | ||
? item.custom | ||
: (0, layout_1.isUintType)(item?.custom?.from) | ||
: (0, layout_1.isNumType)(item?.custom?.from) | ||
? item.custom.from | ||
@@ -38,3 +39,3 @@ : null; | ||
const serialized = new Uint8Array(item.size); | ||
(0, serialize_1.serializeUint)(serialized, 0, fixedVal, item.size); | ||
(0, serialize_1.serializeNum)(serialized, 0, fixedVal, item.size, item.endianness, item.binary === "int"); | ||
return knownFixed(item.size, serialized); | ||
@@ -64,3 +65,3 @@ } | ||
const caseFixedBytes = item.idLayoutPairs.map(_ => []); | ||
const { idSize } = item; | ||
const { idSize, idEndianness } = item; | ||
const caseBounds = item.idLayoutPairs.map(([idOrConversionId, layout], caseIndex) => { | ||
@@ -70,3 +71,3 @@ const idVal = Array.isArray(idOrConversionId) ? idOrConversionId[0] : idOrConversionId; | ||
const serializedId = new Uint8Array(idSize); | ||
(0, serialize_1.serializeUint)(serializedId, 0, idVal, idSize); | ||
(0, serialize_1.serializeNum)(serializedId, 0, idVal, idSize, idEndianness); | ||
caseFixedBytes[caseIndex].push([0, serializedId]); | ||
@@ -73,0 +74,0 @@ } |
@@ -7,2 +7,3 @@ "use strict"; | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": | ||
@@ -47,2 +48,3 @@ case "bytes": { | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": | ||
@@ -49,0 +51,0 @@ case "bytes": { |
@@ -1,11 +0,12 @@ | ||
export type UintType = number | bigint; | ||
export declare const isUintType: (x: any) => x is UintType; | ||
export type NumType = number | bigint; | ||
export declare const isNumType: (x: any) => x is NumType; | ||
export type BytesType = Uint8Array; | ||
export declare const isBytesType: (x: any) => x is Uint8Array; | ||
export type PrimitiveType = UintType | BytesType; | ||
export type PrimitiveType = NumType | BytesType; | ||
export declare const isPrimitiveType: (x: any) => x is PrimitiveType; | ||
export type BinaryLiterals = "uint" | "bytes" | "array" | "object" | "switch"; | ||
export type BinaryLiterals = "int" | "uint" | "bytes" | "array" | "object" | "switch"; | ||
export type Endianness = "little" | "big"; | ||
export type NumberSize = 1 | 2 | 3 | 4 | 5 | 6; | ||
export declare const numberMaxSize = 6; | ||
export type UintSizeToPrimitive<Size extends number> = Size extends NumberSize ? number : bigint; | ||
export type NumSizeToPrimitive<Size extends number> = Size extends NumberSize ? number : bigint; | ||
export type FixedConversion<FromType extends PrimitiveType, ToType> = { | ||
@@ -29,8 +30,9 @@ readonly to: ToType; | ||
} | ||
interface UintLayoutItemBase<T extends UintType> extends LayoutItemBase<"uint"> { | ||
interface NumLayoutItemBase<T extends NumType, Signed extends Boolean> extends LayoutItemBase<Signed extends true ? "int" : "uint"> { | ||
size: T extends bigint ? number : NumberSize; | ||
endianness?: Endianness; | ||
} | ||
export interface PrimitiveFixedUintLayoutItem<T extends UintType> extends UintLayoutItemBase<T>, PrimitiveFixedCustom<T> { | ||
interface PrimitiveFixedNumLayoutItem<T extends NumType, Signed extends Boolean> extends NumLayoutItemBase<T, Signed>, PrimitiveFixedCustom<T> { | ||
} | ||
export interface OptionalToFromUintLayoutItem<T extends UintType> extends UintLayoutItemBase<T>, OptionalToFromCustom<T> { | ||
interface OptionalToFromNumLayoutItem<T extends NumType, Signed extends Boolean> extends NumLayoutItemBase<T, Signed>, OptionalToFromCustom<T> { | ||
} | ||
@@ -48,2 +50,3 @@ export interface FixedPrimitiveBytesLayoutItem extends LayoutItemBase<"bytes">, PrimitiveFixedCustom<BytesType> { | ||
readonly lengthSize?: NumberSize; | ||
readonly lengthEndianness?: Endianness; | ||
readonly custom?: CustomConversion<BytesType, any>; | ||
@@ -53,2 +56,3 @@ } | ||
readonly lengthSize?: NumberSize; | ||
readonly lengthEndianness?: Endianness; | ||
readonly arrayItem: LayoutItem; | ||
@@ -63,9 +67,12 @@ } | ||
export interface SwitchLayoutItem extends LayoutItemBase<"switch"> { | ||
readonly idSize: NumberSize; | ||
readonly idTag?: string; | ||
readonly idSize: NumberSize; | ||
readonly idEndianness?: Endianness; | ||
readonly idLayoutPairs: readonly IdLayoutPair<PlainId>[] | readonly IdLayoutPair<ConversionId>[]; | ||
} | ||
export type UintLayoutItem = PrimitiveFixedUintLayoutItem<number> | OptionalToFromUintLayoutItem<number> | PrimitiveFixedUintLayoutItem<bigint> | OptionalToFromUintLayoutItem<bigint>; | ||
type NumLayoutItem<Signed extends boolean = boolean> = PrimitiveFixedNumLayoutItem<number, Signed> | OptionalToFromNumLayoutItem<number, Signed> | PrimitiveFixedNumLayoutItem<bigint, Signed> | OptionalToFromNumLayoutItem<bigint, Signed>; | ||
export type IntLayoutItem = NumLayoutItem<true>; | ||
export type UintLayoutItem = NumLayoutItem<false>; | ||
export type BytesLayoutItem = FixedPrimitiveBytesLayoutItem | FixedValueBytesLayoutItem | FixedSizeBytesLayoutItem | LengthPrefixedBytesLayoutItem; | ||
export type LayoutItem = UintLayoutItem | BytesLayoutItem | ArrayLayoutItem | ObjectLayoutItem | SwitchLayoutItem; | ||
export type LayoutItem = IntLayoutItem | UintLayoutItem | BytesLayoutItem | ArrayLayoutItem | ObjectLayoutItem | SwitchLayoutItem; | ||
export type NamedLayoutItem = LayoutItem & { | ||
@@ -90,4 +97,4 @@ readonly name: string; | ||
I | ||
] extends [UintLayoutItem] ? I["custom"] extends UintType ? I["custom"] : I["custom"] extends CustomConversion<infer FromType extends UintType, infer ToType> ? ToType : I["custom"] extends FixedConversion<infer FromType extends UintType, infer ToType> ? ToType : UintSizeToPrimitive<I["size"]> : [I] extends [BytesLayoutItem] ? I["custom"] extends CustomConversion<BytesType, infer ToType> ? ToType : I["custom"] extends FixedConversion<BytesType, infer ToType> ? ToType : BytesType : [I] extends [ArrayLayoutItem] ? readonly LayoutItemToType<I["arrayItem"]>[] : [I] extends [ObjectLayoutItem] ? LayoutToType<I["layout"]> : [I] extends [SwitchLayoutItem] ? IdLayoutPairsToTypeUnion<I["idLayoutPairs"], I["idTag"] extends string ? I["idTag"] : "id"> : never; | ||
] extends [NumLayoutItem] ? I["custom"] extends NumType ? I["custom"] : I["custom"] extends CustomConversion<infer FromType extends NumType, infer ToType> ? ToType : I["custom"] extends FixedConversion<infer FromType extends NumType, infer ToType> ? ToType : NumSizeToPrimitive<I["size"]> : [I] extends [BytesLayoutItem] ? I["custom"] extends CustomConversion<BytesType, infer ToType> ? ToType : I["custom"] extends FixedConversion<BytesType, infer ToType> ? ToType : BytesType : [I] extends [ArrayLayoutItem] ? readonly LayoutItemToType<I["arrayItem"]>[] : [I] extends [ObjectLayoutItem] ? LayoutToType<I["layout"]> : [I] extends [SwitchLayoutItem] ? IdLayoutPairsToTypeUnion<I["idLayoutPairs"], I["idTag"] extends string ? I["idTag"] : "id"> : never; | ||
export {}; | ||
//# sourceMappingURL=layout.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.numberMaxSize = exports.isPrimitiveType = exports.isBytesType = exports.isUintType = void 0; | ||
const isUintType = (x) => typeof x === "number" || typeof x === "bigint"; | ||
exports.isUintType = isUintType; | ||
exports.numberMaxSize = exports.isPrimitiveType = exports.isBytesType = exports.isNumType = void 0; | ||
const isNumType = (x) => typeof x === "number" || typeof x === "bigint"; | ||
exports.isNumType = isNumType; | ||
const isBytesType = (x) => x instanceof Uint8Array; | ||
exports.isBytesType = isBytesType; | ||
const isPrimitiveType = (x) => (0, exports.isUintType)(x) || (0, exports.isBytesType)(x); | ||
const isPrimitiveType = (x) => (0, exports.isNumType)(x) || (0, exports.isBytesType)(x); | ||
exports.isPrimitiveType = isPrimitiveType; | ||
@@ -10,0 +10,0 @@ exports.numberMaxSize = 6; |
@@ -1,5 +0,5 @@ | ||
import { Layout, LayoutToType, UintType } from "./layout"; | ||
import { Endianness, Layout, LayoutToType, NumType } from "./layout"; | ||
export declare function serializeLayout<const L extends Layout>(layout: L, data: LayoutToType<L>): Uint8Array; | ||
export declare function serializeLayout<const L extends Layout>(layout: L, data: LayoutToType<L>, encoded: Uint8Array, offset?: number): number; | ||
export declare function serializeUint(encoded: Uint8Array, offset: number, val: UintType, bytes: number): number; | ||
export declare function serializeNum(encoded: Uint8Array, offset: number, val: NumType, bytes: number, endianness?: Endianness, signed?: boolean): number; | ||
//# sourceMappingURL=serialize.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.serializeUint = exports.serializeLayout = void 0; | ||
exports.serializeNum = exports.serializeLayout = void 0; | ||
const layout_1 = require("./layout"); | ||
@@ -20,2 +20,3 @@ const utils_1 = require("./utils"); | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": { | ||
@@ -57,38 +58,49 @@ return item.size; | ||
const calcLayoutSize = (layout, data) => layout.reduce((acc, item) => acc + calcItemSize(item, data[item.name]), 0); | ||
//Wormhole uses big endian by default for all uints | ||
//endianess can be easily added to UintLayout items if necessary | ||
function serializeUint(encoded, offset, val, bytes) { | ||
if (val < 0 || (typeof val === "number" && !Number.isInteger(val))) | ||
throw new Error(`Value ${val} is not an unsigned integer`); | ||
if (bytes > layout_1.numberMaxSize && typeof val === "number" && val >= 2 ** (layout_1.numberMaxSize * 8)) | ||
throw new Error(`Value ${val} is too large to be safely converted into an integer`); | ||
if (val >= 2n ** BigInt(bytes * 8)) | ||
//see numberMaxSize comment in layout.ts | ||
const maxAllowedNumberVal = 2 ** (layout_1.numberMaxSize * 8); | ||
function serializeNum(encoded, offset, val, bytes, endianness = "big", signed = false) { | ||
if (!signed && val < 0) | ||
throw new Error(`Value ${val} is negative but unsigned`); | ||
if (typeof val === "number") { | ||
if (!Number.isInteger(val)) | ||
throw new Error(`Value ${val} is not an integer`); | ||
if (bytes > layout_1.numberMaxSize) { | ||
if (val >= maxAllowedNumberVal) | ||
throw new Error(`Value ${val} is too large to be safely converted into an integer`); | ||
if (signed && val <= -maxAllowedNumberVal) | ||
throw new Error(`Value ${val} is too small to be safely converted into an integer`); | ||
} | ||
} | ||
const bound = 2n ** BigInt(bytes * 8); | ||
if (val >= bound) | ||
throw new Error(`Value ${val} is too large for ${bytes} bytes`); | ||
//big endian byte order | ||
if (signed && val < -bound) | ||
throw new Error(`Value ${val} is too small for ${bytes} bytes`); | ||
//correctly handles both signed and unsigned values | ||
for (let i = 0; i < bytes; ++i) | ||
encoded[offset + i] = Number((BigInt(val) >> BigInt(8 * (bytes - i - 1)) & 0xffn)); | ||
encoded[offset + i] = | ||
Number((BigInt(val) >> BigInt(8 * (endianness === "big" ? bytes - i - 1 : i)) & 0xffn)); | ||
return offset + bytes; | ||
} | ||
exports.serializeUint = serializeUint; | ||
exports.serializeNum = serializeNum; | ||
function serializeLayoutItem(item, data, encoded, offset) { | ||
try { | ||
switch (item.binary) { | ||
case "switch": { | ||
const [idOrConversionId, layout] = findIdLayoutPair(item, data); | ||
const idNum = (Array.isArray(idOrConversionId) ? idOrConversionId[0] : idOrConversionId); | ||
offset = serializeUint(encoded, offset, idNum, item.idSize); | ||
offset = serializeLayout(layout, data, encoded, offset); | ||
case "int": | ||
case "uint": { | ||
const value = (() => { | ||
if ((0, layout_1.isNumType)(item.custom)) { | ||
if (!item?.omit) | ||
(0, utils_1.checkNumEquals)(item.custom, data); | ||
return item.custom; | ||
} | ||
if ((0, layout_1.isNumType)(item?.custom?.from)) | ||
//no proper way to deeply check equality of item.custom.to and data in JS | ||
return item.custom.from; | ||
return item.custom !== undefined ? item.custom.from(data) : data; | ||
})(); | ||
offset = | ||
serializeNum(encoded, offset, value, item.size, item.endianness, item.binary === "int"); | ||
break; | ||
} | ||
case "object": { | ||
offset = serializeLayout(item.layout, data, encoded, offset); | ||
break; | ||
} | ||
case "array": { | ||
if (item.lengthSize !== undefined) | ||
offset = serializeUint(encoded, offset, data.length, item.lengthSize); | ||
for (let i = 0; i < data.length; ++i) | ||
offset = serializeLayoutItem(withIgnoredName(item.arrayItem), data[i], encoded, offset); | ||
break; | ||
} | ||
case "bytes": { | ||
@@ -109,3 +121,4 @@ const value = (() => { | ||
else if (item.lengthSize !== undefined) | ||
offset = serializeUint(encoded, offset, ret.length, item.lengthSize); | ||
offset = | ||
serializeNum(encoded, offset, ret.length, item.lengthSize, item.lengthEndianness); | ||
return ret; | ||
@@ -117,17 +130,21 @@ })(); | ||
} | ||
case "uint": { | ||
const value = (() => { | ||
if ((0, layout_1.isUintType)(item.custom)) { | ||
if (!item?.omit) | ||
(0, utils_1.checkUintEquals)(item.custom, data); | ||
return item.custom; | ||
} | ||
if ((0, layout_1.isUintType)(item?.custom?.from)) | ||
//no proper way to deeply check equality of item.custom.to and data in JS | ||
return item.custom.from; | ||
return item.custom !== undefined ? item.custom.from(data) : data; | ||
})(); | ||
offset = serializeUint(encoded, offset, value, item.size); | ||
case "array": { | ||
if (item.lengthSize !== undefined) | ||
offset = | ||
serializeNum(encoded, offset, data.length, item.lengthSize, item.lengthEndianness); | ||
for (let i = 0; i < data.length; ++i) | ||
offset = serializeLayoutItem(withIgnoredName(item.arrayItem), data[i], encoded, offset); | ||
break; | ||
} | ||
case "object": { | ||
offset = serializeLayout(item.layout, data, encoded, offset); | ||
break; | ||
} | ||
case "switch": { | ||
const [idOrConversionId, layout] = findIdLayoutPair(item, data); | ||
const idNum = (Array.isArray(idOrConversionId) ? idOrConversionId[0] : idOrConversionId); | ||
offset = serializeNum(encoded, offset, idNum, item.idSize, item.idEndianness); | ||
offset = serializeLayout(layout, data, encoded, offset); | ||
break; | ||
} | ||
} | ||
@@ -134,0 +151,0 @@ } |
export declare const checkUint8ArraySize: (custom: Uint8Array, size: number) => void; | ||
export declare const checkUintEquals: (custom: number | bigint, data: number | bigint) => void; | ||
export declare const checkNumEquals: (custom: number | bigint, data: number | bigint) => void; | ||
export declare const checkUint8ArrayDeeplyEqual: (custom: Uint8Array, data: Uint8Array) => void; | ||
//# sourceMappingURL=utils.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.checkUint8ArrayDeeplyEqual = exports.checkUintEquals = exports.checkUint8ArraySize = void 0; | ||
exports.checkUint8ArrayDeeplyEqual = exports.checkNumEquals = exports.checkUint8ArraySize = void 0; | ||
const checkUint8ArraySize = (custom, size) => { | ||
@@ -9,7 +9,7 @@ if (custom.length !== size) | ||
exports.checkUint8ArraySize = checkUint8ArraySize; | ||
const checkUintEquals = (custom, data) => { | ||
const checkNumEquals = (custom, data) => { | ||
if (custom != data) | ||
throw new Error(`value mismatch: (constant) layout value: ${custom}, data value: ${data}`); | ||
}; | ||
exports.checkUintEquals = checkUintEquals; | ||
exports.checkNumEquals = checkNumEquals; | ||
const checkUint8ArrayDeeplyEqual = (custom, data) => { | ||
@@ -16,0 +16,0 @@ (0, exports.checkUint8ArraySize)(custom, data.length); |
@@ -1,4 +0,4 @@ | ||
export declare const protocols: readonly ["WormholeCore", "TokenBridge", "AutomaticTokenBridge", "CircleBridge", "AutomaticCircleBridge", "Relayer", "NftBridge"]; | ||
export declare const protocols: readonly ["WormholeCore", "TokenBridge", "AutomaticTokenBridge", "CircleBridge", "AutomaticCircleBridge", "Relayer", "IbcBridge", "NftBridge"]; | ||
export type ProtocolName = (typeof protocols)[number]; | ||
export declare const isProtocolName: (protocol: string) => protocol is "WormholeCore" | "TokenBridge" | "AutomaticTokenBridge" | "CircleBridge" | "AutomaticCircleBridge" | "Relayer" | "NftBridge"; | ||
export declare const isProtocolName: (protocol: string) => protocol is "WormholeCore" | "TokenBridge" | "AutomaticTokenBridge" | "CircleBridge" | "AutomaticCircleBridge" | "Relayer" | "IbcBridge" | "NftBridge"; | ||
//# sourceMappingURL=protocols.d.ts.map |
@@ -32,2 +32,3 @@ /* TODO: | ||
"Relayer", | ||
"IbcBridge", | ||
// not implemented | ||
@@ -34,0 +35,0 @@ "NftBridge", |
@@ -1,3 +0,3 @@ | ||
import { isUintType, isBytesType, numberMaxSize, } from "./layout"; | ||
import { checkUint8ArrayDeeplyEqual, checkUintEquals } from "./utils"; | ||
import { isNumType, isBytesType, numberMaxSize, } from "./layout"; | ||
import { checkUint8ArrayDeeplyEqual, checkNumEquals } from "./utils"; | ||
export function deserializeLayout(layout, encoded, offset = 0, consumeAll = true) { | ||
@@ -22,9 +22,12 @@ const [decoded, finalOffset] = internalDeserializeLayout(layout, encoded, offset); | ||
} | ||
function deserializeUint(encoded, offset, size) { | ||
let value = 0n; | ||
for (let i = 0; i < size; ++i) | ||
value += BigInt(encoded[offset + i]) << BigInt((size - 1 - i) * 8); | ||
function deserializeNum(encoded, offset, bytes, endianness = "big", signed = false) { | ||
let val = 0n; | ||
for (let i = 0; i < bytes; ++i) | ||
val |= BigInt(encoded[offset + i]) << BigInt(8 * (endianness === "big" ? bytes - i - 1 : i)); | ||
//check sign bit if value is indeed signed and adjust accordingly | ||
if (signed && (encoded[offset + (endianness === "big" ? 0 : bytes - 1)] & 0x80)) | ||
val -= 1n << BigInt(8 * bytes); | ||
return [ | ||
((size > numberMaxSize) ? value : Number(value)), | ||
updateOffset(encoded, offset, size) | ||
((bytes > numberMaxSize) ? val : Number(val)), | ||
updateOffset(encoded, offset, bytes) | ||
]; | ||
@@ -35,10 +38,11 @@ } | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": { | ||
const [value, newOffset] = deserializeUint(encoded, offset, item.size); | ||
if (isUintType(item.custom)) { | ||
checkUintEquals(item.custom, value); | ||
const [value, newOffset] = deserializeNum(encoded, offset, item.size, item.endianness, item.binary === "int"); | ||
if (isNumType(item.custom)) { | ||
checkNumEquals(item.custom, value); | ||
return [item.custom, newOffset]; | ||
} | ||
if (isUintType(item?.custom?.from)) { | ||
checkUintEquals(item.custom.from, value); | ||
if (isNumType(item?.custom?.from)) { | ||
checkNumEquals(item.custom.from, value); | ||
return [item.custom.to, newOffset]; | ||
@@ -71,3 +75,4 @@ } | ||
let length; | ||
[length, offset] = deserializeUint(encoded, offset, item.lengthSize); | ||
[length, offset] = | ||
deserializeNum(encoded, offset, item.lengthSize, item.lengthEndianness); | ||
newOffset = updateOffset(encoded, offset, length); | ||
@@ -97,3 +102,3 @@ } | ||
if (item.lengthSize !== undefined) { | ||
const [length, newOffset] = deserializeUint(encoded, offset, item.lengthSize); | ||
const [length, newOffset] = deserializeNum(encoded, offset, item.lengthSize, item.lengthEndianness); | ||
offset = newOffset; | ||
@@ -112,3 +117,3 @@ for (let i = 0; i < length; ++i) | ||
case "switch": { | ||
const [id, newOffset] = deserializeUint(encoded, offset, item.idSize); | ||
const [id, newOffset] = deserializeNum(encoded, offset, item.idSize, item.idEndianness); | ||
const { idLayoutPairs } = item; | ||
@@ -115,0 +120,0 @@ if (idLayoutPairs.length === 0) |
@@ -1,3 +0,3 @@ | ||
import { isUintType, isBytesType, } from "./layout"; | ||
import { serializeUint } from "./serialize"; | ||
import { isNumType, isBytesType, } from "./layout"; | ||
import { serializeNum } from "./serialize"; | ||
function arrayToBitset(arr) { | ||
@@ -26,6 +26,7 @@ return arr.reduce((bit, i) => bit | BigInt(1) << BigInt(i), BigInt(0)); | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": { | ||
const fixedVal = isUintType(item.custom) | ||
const fixedVal = isNumType(item.custom) | ||
? item.custom | ||
: isUintType(item?.custom?.from) | ||
: isNumType(item?.custom?.from) | ||
? item.custom.from | ||
@@ -35,3 +36,3 @@ : null; | ||
const serialized = new Uint8Array(item.size); | ||
serializeUint(serialized, 0, fixedVal, item.size); | ||
serializeNum(serialized, 0, fixedVal, item.size, item.endianness, item.binary === "int"); | ||
return knownFixed(item.size, serialized); | ||
@@ -61,3 +62,3 @@ } | ||
const caseFixedBytes = item.idLayoutPairs.map(_ => []); | ||
const { idSize } = item; | ||
const { idSize, idEndianness } = item; | ||
const caseBounds = item.idLayoutPairs.map(([idOrConversionId, layout], caseIndex) => { | ||
@@ -67,3 +68,3 @@ const idVal = Array.isArray(idOrConversionId) ? idOrConversionId[0] : idOrConversionId; | ||
const serializedId = new Uint8Array(idSize); | ||
serializeUint(serializedId, 0, idVal, idSize); | ||
serializeNum(serializedId, 0, idVal, idSize, idEndianness); | ||
caseFixedBytes[caseIndex].push([0, serializedId]); | ||
@@ -70,0 +71,0 @@ } |
import { isPrimitiveType, } from "./layout"; | ||
function filterItem(item, fixed) { | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": | ||
@@ -41,2 +42,3 @@ case "bytes": { | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": | ||
@@ -43,0 +45,0 @@ case "bytes": { |
@@ -1,11 +0,12 @@ | ||
export type UintType = number | bigint; | ||
export declare const isUintType: (x: any) => x is UintType; | ||
export type NumType = number | bigint; | ||
export declare const isNumType: (x: any) => x is NumType; | ||
export type BytesType = Uint8Array; | ||
export declare const isBytesType: (x: any) => x is Uint8Array; | ||
export type PrimitiveType = UintType | BytesType; | ||
export type PrimitiveType = NumType | BytesType; | ||
export declare const isPrimitiveType: (x: any) => x is PrimitiveType; | ||
export type BinaryLiterals = "uint" | "bytes" | "array" | "object" | "switch"; | ||
export type BinaryLiterals = "int" | "uint" | "bytes" | "array" | "object" | "switch"; | ||
export type Endianness = "little" | "big"; | ||
export type NumberSize = 1 | 2 | 3 | 4 | 5 | 6; | ||
export declare const numberMaxSize = 6; | ||
export type UintSizeToPrimitive<Size extends number> = Size extends NumberSize ? number : bigint; | ||
export type NumSizeToPrimitive<Size extends number> = Size extends NumberSize ? number : bigint; | ||
export type FixedConversion<FromType extends PrimitiveType, ToType> = { | ||
@@ -29,8 +30,9 @@ readonly to: ToType; | ||
} | ||
interface UintLayoutItemBase<T extends UintType> extends LayoutItemBase<"uint"> { | ||
interface NumLayoutItemBase<T extends NumType, Signed extends Boolean> extends LayoutItemBase<Signed extends true ? "int" : "uint"> { | ||
size: T extends bigint ? number : NumberSize; | ||
endianness?: Endianness; | ||
} | ||
export interface PrimitiveFixedUintLayoutItem<T extends UintType> extends UintLayoutItemBase<T>, PrimitiveFixedCustom<T> { | ||
interface PrimitiveFixedNumLayoutItem<T extends NumType, Signed extends Boolean> extends NumLayoutItemBase<T, Signed>, PrimitiveFixedCustom<T> { | ||
} | ||
export interface OptionalToFromUintLayoutItem<T extends UintType> extends UintLayoutItemBase<T>, OptionalToFromCustom<T> { | ||
interface OptionalToFromNumLayoutItem<T extends NumType, Signed extends Boolean> extends NumLayoutItemBase<T, Signed>, OptionalToFromCustom<T> { | ||
} | ||
@@ -48,2 +50,3 @@ export interface FixedPrimitiveBytesLayoutItem extends LayoutItemBase<"bytes">, PrimitiveFixedCustom<BytesType> { | ||
readonly lengthSize?: NumberSize; | ||
readonly lengthEndianness?: Endianness; | ||
readonly custom?: CustomConversion<BytesType, any>; | ||
@@ -53,2 +56,3 @@ } | ||
readonly lengthSize?: NumberSize; | ||
readonly lengthEndianness?: Endianness; | ||
readonly arrayItem: LayoutItem; | ||
@@ -63,9 +67,12 @@ } | ||
export interface SwitchLayoutItem extends LayoutItemBase<"switch"> { | ||
readonly idSize: NumberSize; | ||
readonly idTag?: string; | ||
readonly idSize: NumberSize; | ||
readonly idEndianness?: Endianness; | ||
readonly idLayoutPairs: readonly IdLayoutPair<PlainId>[] | readonly IdLayoutPair<ConversionId>[]; | ||
} | ||
export type UintLayoutItem = PrimitiveFixedUintLayoutItem<number> | OptionalToFromUintLayoutItem<number> | PrimitiveFixedUintLayoutItem<bigint> | OptionalToFromUintLayoutItem<bigint>; | ||
type NumLayoutItem<Signed extends boolean = boolean> = PrimitiveFixedNumLayoutItem<number, Signed> | OptionalToFromNumLayoutItem<number, Signed> | PrimitiveFixedNumLayoutItem<bigint, Signed> | OptionalToFromNumLayoutItem<bigint, Signed>; | ||
export type IntLayoutItem = NumLayoutItem<true>; | ||
export type UintLayoutItem = NumLayoutItem<false>; | ||
export type BytesLayoutItem = FixedPrimitiveBytesLayoutItem | FixedValueBytesLayoutItem | FixedSizeBytesLayoutItem | LengthPrefixedBytesLayoutItem; | ||
export type LayoutItem = UintLayoutItem | BytesLayoutItem | ArrayLayoutItem | ObjectLayoutItem | SwitchLayoutItem; | ||
export type LayoutItem = IntLayoutItem | UintLayoutItem | BytesLayoutItem | ArrayLayoutItem | ObjectLayoutItem | SwitchLayoutItem; | ||
export type NamedLayoutItem = LayoutItem & { | ||
@@ -90,4 +97,4 @@ readonly name: string; | ||
I | ||
] extends [UintLayoutItem] ? I["custom"] extends UintType ? I["custom"] : I["custom"] extends CustomConversion<infer FromType extends UintType, infer ToType> ? ToType : I["custom"] extends FixedConversion<infer FromType extends UintType, infer ToType> ? ToType : UintSizeToPrimitive<I["size"]> : [I] extends [BytesLayoutItem] ? I["custom"] extends CustomConversion<BytesType, infer ToType> ? ToType : I["custom"] extends FixedConversion<BytesType, infer ToType> ? ToType : BytesType : [I] extends [ArrayLayoutItem] ? readonly LayoutItemToType<I["arrayItem"]>[] : [I] extends [ObjectLayoutItem] ? LayoutToType<I["layout"]> : [I] extends [SwitchLayoutItem] ? IdLayoutPairsToTypeUnion<I["idLayoutPairs"], I["idTag"] extends string ? I["idTag"] : "id"> : never; | ||
] extends [NumLayoutItem] ? I["custom"] extends NumType ? I["custom"] : I["custom"] extends CustomConversion<infer FromType extends NumType, infer ToType> ? ToType : I["custom"] extends FixedConversion<infer FromType extends NumType, infer ToType> ? ToType : NumSizeToPrimitive<I["size"]> : [I] extends [BytesLayoutItem] ? I["custom"] extends CustomConversion<BytesType, infer ToType> ? ToType : I["custom"] extends FixedConversion<BytesType, infer ToType> ? ToType : BytesType : [I] extends [ArrayLayoutItem] ? readonly LayoutItemToType<I["arrayItem"]>[] : [I] extends [ObjectLayoutItem] ? LayoutToType<I["layout"]> : [I] extends [SwitchLayoutItem] ? IdLayoutPairsToTypeUnion<I["idLayoutPairs"], I["idTag"] extends string ? I["idTag"] : "id"> : never; | ||
export {}; | ||
//# sourceMappingURL=layout.d.ts.map |
@@ -1,4 +0,4 @@ | ||
export const isUintType = (x) => typeof x === "number" || typeof x === "bigint"; | ||
export const isNumType = (x) => typeof x === "number" || typeof x === "bigint"; | ||
export const isBytesType = (x) => x instanceof Uint8Array; | ||
export const isPrimitiveType = (x) => isUintType(x) || isBytesType(x); | ||
export const isPrimitiveType = (x) => isNumType(x) || isBytesType(x); | ||
export const numberMaxSize = 6; | ||
@@ -5,0 +5,0 @@ ; |
@@ -1,5 +0,5 @@ | ||
import { Layout, LayoutToType, UintType } from "./layout"; | ||
import { Endianness, Layout, LayoutToType, NumType } from "./layout"; | ||
export declare function serializeLayout<const L extends Layout>(layout: L, data: LayoutToType<L>): Uint8Array; | ||
export declare function serializeLayout<const L extends Layout>(layout: L, data: LayoutToType<L>, encoded: Uint8Array, offset?: number): number; | ||
export declare function serializeUint(encoded: Uint8Array, offset: number, val: UintType, bytes: number): number; | ||
export declare function serializeNum(encoded: Uint8Array, offset: number, val: NumType, bytes: number, endianness?: Endianness, signed?: boolean): number; | ||
//# sourceMappingURL=serialize.d.ts.map |
@@ -1,3 +0,3 @@ | ||
import { isUintType, isBytesType, numberMaxSize, } from "./layout"; | ||
import { checkUint8ArrayDeeplyEqual, checkUint8ArraySize, checkUintEquals } from "./utils"; | ||
import { isNumType, isBytesType, numberMaxSize, } from "./layout"; | ||
import { checkUint8ArrayDeeplyEqual, checkUint8ArraySize, checkNumEquals } from "./utils"; | ||
export function serializeLayout(layout, data, encoded, offset = 0) { | ||
@@ -16,2 +16,3 @@ let ret = encoded ?? new Uint8Array(calcLayoutSize(layout, data)); | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": { | ||
@@ -53,14 +54,26 @@ return item.size; | ||
const calcLayoutSize = (layout, data) => layout.reduce((acc, item) => acc + calcItemSize(item, data[item.name]), 0); | ||
//Wormhole uses big endian by default for all uints | ||
//endianess can be easily added to UintLayout items if necessary | ||
export function serializeUint(encoded, offset, val, bytes) { | ||
if (val < 0 || (typeof val === "number" && !Number.isInteger(val))) | ||
throw new Error(`Value ${val} is not an unsigned integer`); | ||
if (bytes > numberMaxSize && typeof val === "number" && val >= 2 ** (numberMaxSize * 8)) | ||
throw new Error(`Value ${val} is too large to be safely converted into an integer`); | ||
if (val >= 2n ** BigInt(bytes * 8)) | ||
//see numberMaxSize comment in layout.ts | ||
const maxAllowedNumberVal = 2 ** (numberMaxSize * 8); | ||
export function serializeNum(encoded, offset, val, bytes, endianness = "big", signed = false) { | ||
if (!signed && val < 0) | ||
throw new Error(`Value ${val} is negative but unsigned`); | ||
if (typeof val === "number") { | ||
if (!Number.isInteger(val)) | ||
throw new Error(`Value ${val} is not an integer`); | ||
if (bytes > numberMaxSize) { | ||
if (val >= maxAllowedNumberVal) | ||
throw new Error(`Value ${val} is too large to be safely converted into an integer`); | ||
if (signed && val <= -maxAllowedNumberVal) | ||
throw new Error(`Value ${val} is too small to be safely converted into an integer`); | ||
} | ||
} | ||
const bound = 2n ** BigInt(bytes * 8); | ||
if (val >= bound) | ||
throw new Error(`Value ${val} is too large for ${bytes} bytes`); | ||
//big endian byte order | ||
if (signed && val < -bound) | ||
throw new Error(`Value ${val} is too small for ${bytes} bytes`); | ||
//correctly handles both signed and unsigned values | ||
for (let i = 0; i < bytes; ++i) | ||
encoded[offset + i] = Number((BigInt(val) >> BigInt(8 * (bytes - i - 1)) & 0xffn)); | ||
encoded[offset + i] = | ||
Number((BigInt(val) >> BigInt(8 * (endianness === "big" ? bytes - i - 1 : i)) & 0xffn)); | ||
return offset + bytes; | ||
@@ -71,20 +84,19 @@ } | ||
switch (item.binary) { | ||
case "switch": { | ||
const [idOrConversionId, layout] = findIdLayoutPair(item, data); | ||
const idNum = (Array.isArray(idOrConversionId) ? idOrConversionId[0] : idOrConversionId); | ||
offset = serializeUint(encoded, offset, idNum, item.idSize); | ||
offset = serializeLayout(layout, data, encoded, offset); | ||
case "int": | ||
case "uint": { | ||
const value = (() => { | ||
if (isNumType(item.custom)) { | ||
if (!item?.omit) | ||
checkNumEquals(item.custom, data); | ||
return item.custom; | ||
} | ||
if (isNumType(item?.custom?.from)) | ||
//no proper way to deeply check equality of item.custom.to and data in JS | ||
return item.custom.from; | ||
return item.custom !== undefined ? item.custom.from(data) : data; | ||
})(); | ||
offset = | ||
serializeNum(encoded, offset, value, item.size, item.endianness, item.binary === "int"); | ||
break; | ||
} | ||
case "object": { | ||
offset = serializeLayout(item.layout, data, encoded, offset); | ||
break; | ||
} | ||
case "array": { | ||
if (item.lengthSize !== undefined) | ||
offset = serializeUint(encoded, offset, data.length, item.lengthSize); | ||
for (let i = 0; i < data.length; ++i) | ||
offset = serializeLayoutItem(withIgnoredName(item.arrayItem), data[i], encoded, offset); | ||
break; | ||
} | ||
case "bytes": { | ||
@@ -105,3 +117,4 @@ const value = (() => { | ||
else if (item.lengthSize !== undefined) | ||
offset = serializeUint(encoded, offset, ret.length, item.lengthSize); | ||
offset = | ||
serializeNum(encoded, offset, ret.length, item.lengthSize, item.lengthEndianness); | ||
return ret; | ||
@@ -113,17 +126,21 @@ })(); | ||
} | ||
case "uint": { | ||
const value = (() => { | ||
if (isUintType(item.custom)) { | ||
if (!item?.omit) | ||
checkUintEquals(item.custom, data); | ||
return item.custom; | ||
} | ||
if (isUintType(item?.custom?.from)) | ||
//no proper way to deeply check equality of item.custom.to and data in JS | ||
return item.custom.from; | ||
return item.custom !== undefined ? item.custom.from(data) : data; | ||
})(); | ||
offset = serializeUint(encoded, offset, value, item.size); | ||
case "array": { | ||
if (item.lengthSize !== undefined) | ||
offset = | ||
serializeNum(encoded, offset, data.length, item.lengthSize, item.lengthEndianness); | ||
for (let i = 0; i < data.length; ++i) | ||
offset = serializeLayoutItem(withIgnoredName(item.arrayItem), data[i], encoded, offset); | ||
break; | ||
} | ||
case "object": { | ||
offset = serializeLayout(item.layout, data, encoded, offset); | ||
break; | ||
} | ||
case "switch": { | ||
const [idOrConversionId, layout] = findIdLayoutPair(item, data); | ||
const idNum = (Array.isArray(idOrConversionId) ? idOrConversionId[0] : idOrConversionId); | ||
offset = serializeNum(encoded, offset, idNum, item.idSize, item.idEndianness); | ||
offset = serializeLayout(layout, data, encoded, offset); | ||
break; | ||
} | ||
} | ||
@@ -130,0 +147,0 @@ } |
export declare const checkUint8ArraySize: (custom: Uint8Array, size: number) => void; | ||
export declare const checkUintEquals: (custom: number | bigint, data: number | bigint) => void; | ||
export declare const checkNumEquals: (custom: number | bigint, data: number | bigint) => void; | ||
export declare const checkUint8ArrayDeeplyEqual: (custom: Uint8Array, data: Uint8Array) => void; | ||
//# sourceMappingURL=utils.d.ts.map |
@@ -5,3 +5,3 @@ export const checkUint8ArraySize = (custom, size) => { | ||
}; | ||
export const checkUintEquals = (custom, data) => { | ||
export const checkNumEquals = (custom, data) => { | ||
if (custom != data) | ||
@@ -8,0 +8,0 @@ throw new Error(`value mismatch: (constant) layout value: ${custom}, data value: ${data}`); |
{ | ||
"name": "@wormhole-foundation/sdk-base", | ||
"version": "0.1.8-beta.7", | ||
"version": "0.1.8-beta.8", | ||
"repository": { | ||
@@ -18,4 +18,4 @@ "type": "git", | ||
"module": "./dist/esm/index.js", | ||
"types":"./dist/esm/index.d.ts", | ||
"files":[ | ||
"types": "./dist/esm/index.d.ts", | ||
"files": [ | ||
"dist/**/*", | ||
@@ -25,3 +25,3 @@ "src/**/*" | ||
"dependencies": { | ||
"@scure/base":"^1.1.3" | ||
"@scure/base": "^1.1.3" | ||
}, | ||
@@ -31,10 +31,11 @@ "sideEffects": false, | ||
"test": "jest --config ../../jest.config.ts __tests__/*.ts", | ||
"build:cjs":"tsc -p ./tsconfig.cjs.json", | ||
"build:esm":"tsc -p ./tsconfig.esm.json", | ||
"build":"npm run build:cjs && npm run build:esm", | ||
"rebuild":"npm run clean && npm run build:cjs && npm run build:esm", | ||
"build:cjs": "tsc -p ./tsconfig.cjs.json", | ||
"build:esm": "tsc -p ./tsconfig.esm.json", | ||
"build": "npm run build:cjs && npm run build:esm", | ||
"rebuild": "npm run clean && npm run build:cjs && npm run build:esm", | ||
"clean": "rm -rf ./dist && rm -f ./*.tsbuildinfo", | ||
"lint": "npm run prettier && eslint --fix", | ||
"bump:beta": "bump prerelease", | ||
"prettier": "prettier --write ./src" | ||
} | ||
} |
@@ -33,2 +33,3 @@ /* TODO: | ||
"Relayer", | ||
"IbcBridge", | ||
// not implemented | ||
@@ -35,0 +36,0 @@ "NftBridge", |
import { | ||
Endianness, | ||
Layout, | ||
NamedLayoutItem, | ||
LayoutToType, | ||
LayoutItemToType, | ||
FixedPrimitiveBytesLayoutItem, | ||
FixedValueBytesLayoutItem, | ||
CustomConversion, | ||
UintSizeToPrimitive, | ||
UintType, | ||
NumSizeToPrimitive, | ||
NumType, | ||
BytesType, | ||
isUintType, | ||
isNumType, | ||
isBytesType, | ||
@@ -17,3 +17,3 @@ numberMaxSize, | ||
import { checkUint8ArrayDeeplyEqual, checkUintEquals } from "./utils"; | ||
import { checkUint8ArrayDeeplyEqual, checkNumEquals } from "./utils"; | ||
@@ -73,14 +73,20 @@ export function deserializeLayout<const L extends Layout>( | ||
function deserializeUint<S extends number>( | ||
function deserializeNum<S extends number>( | ||
encoded: Uint8Array, | ||
offset: number, | ||
size: S, | ||
): readonly [UintSizeToPrimitive<S>, number] { | ||
let value = 0n; | ||
for (let i = 0; i < size; ++i) | ||
value += BigInt(encoded[offset + i]) << BigInt((size - 1 - i) * 8); | ||
bytes: S, | ||
endianness: Endianness = "big", | ||
signed: boolean = false, | ||
): readonly [NumSizeToPrimitive<S>, number] { | ||
let val = 0n; | ||
for (let i = 0; i < bytes; ++i) | ||
val |= BigInt(encoded[offset + i]) << BigInt(8 * (endianness === "big" ? bytes-i-1 : i)); | ||
//check sign bit if value is indeed signed and adjust accordingly | ||
if (signed && (encoded[offset + (endianness === "big" ? 0 : bytes-1)] & 0x80)) | ||
val -= 1n << BigInt(8 * bytes); | ||
return [ | ||
((size > numberMaxSize) ? value : Number(value)) as UintSizeToPrimitive<S>, | ||
updateOffset(encoded, offset, size) | ||
((bytes > numberMaxSize) ? val : Number(val)) as NumSizeToPrimitive<S>, | ||
updateOffset(encoded, offset, bytes) | ||
] as const; | ||
@@ -96,12 +102,14 @@ } | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": { | ||
const [value, newOffset] = deserializeUint(encoded, offset, item.size); | ||
const [value, newOffset] = | ||
deserializeNum(encoded, offset, item.size, item.endianness, item.binary === "int"); | ||
if (isUintType(item.custom)) { | ||
checkUintEquals(item.custom, value); | ||
if (isNumType(item.custom)) { | ||
checkNumEquals(item.custom, value); | ||
return [item.custom, newOffset]; | ||
} | ||
if (isUintType(item?.custom?.from)) { | ||
checkUintEquals(item!.custom!.from, value); | ||
if (isNumType(item?.custom?.from)) { | ||
checkNumEquals(item!.custom!.from, value); | ||
return [item!.custom!.to, newOffset]; | ||
@@ -113,3 +121,3 @@ } | ||
// further tease that apart still for no real gain... | ||
type narrowedCustom = CustomConversion<UintType, any>; | ||
type narrowedCustom = CustomConversion<NumType, any>; | ||
return [ | ||
@@ -142,3 +150,4 @@ item.custom !== undefined ? (item.custom as narrowedCustom).to(value) : value, | ||
let length; | ||
[length, offset] = deserializeUint(encoded, offset, item.lengthSize); | ||
[length, offset] = | ||
deserializeNum(encoded, offset, item.lengthSize, item.lengthEndianness); | ||
newOffset = updateOffset(encoded, offset, length); | ||
@@ -176,3 +185,4 @@ } | ||
if (item.lengthSize !== undefined) { | ||
const [length, newOffset] = deserializeUint(encoded, offset, item.lengthSize); | ||
const [length, newOffset] = | ||
deserializeNum(encoded, offset, item.lengthSize, item.lengthEndianness); | ||
offset = newOffset; | ||
@@ -192,3 +202,3 @@ for (let i = 0; i < length; ++i) | ||
case "switch": { | ||
const [id, newOffset] = deserializeUint(encoded, offset, item.idSize); | ||
const [id, newOffset] = deserializeNum(encoded, offset, item.idSize, item.idEndianness); | ||
const {idLayoutPairs} = item; | ||
@@ -195,0 +205,0 @@ if (idLayoutPairs.length === 0) |
@@ -5,7 +5,7 @@ import { | ||
LengthPrefixedBytesLayoutItem, | ||
isUintType, | ||
isNumType, | ||
isBytesType, | ||
} from "./layout"; | ||
import { serializeUint } from "./serialize"; | ||
import { serializeNum } from "./serialize"; | ||
@@ -60,7 +60,8 @@ //defining a bunch of types for readability | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": { | ||
const fixedVal = | ||
isUintType(item.custom) | ||
isNumType(item.custom) | ||
? item.custom | ||
: isUintType(item?.custom?.from) | ||
: isNumType(item?.custom?.from) | ||
? item!.custom!.from | ||
@@ -71,3 +72,3 @@ : null; | ||
const serialized = new Uint8Array(item.size); | ||
serializeUint(serialized, 0, fixedVal, item.size); | ||
serializeNum(serialized, 0, fixedVal, item.size, item.endianness, item.binary === "int"); | ||
return knownFixed(item.size, serialized); | ||
@@ -101,3 +102,3 @@ } | ||
const caseFixedBytes = item.idLayoutPairs.map(_ => []) as FixedBytes[]; | ||
const {idSize} = item; | ||
const {idSize, idEndianness} = item; | ||
const caseBounds = item.idLayoutPairs.map(([idOrConversionId, layout], caseIndex) => { | ||
@@ -107,3 +108,3 @@ const idVal = Array.isArray(idOrConversionId) ? idOrConversionId[0] : idOrConversionId; | ||
const serializedId = new Uint8Array(idSize); | ||
serializeUint(serializedId, 0, idVal, idSize); | ||
serializeNum(serializedId, 0, idVal, idSize, idEndianness); | ||
caseFixedBytes[caseIndex].push([0, serializedId]); | ||
@@ -110,0 +111,0 @@ } |
@@ -66,2 +66,3 @@ import { | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": | ||
@@ -128,2 +129,3 @@ case "bytes": { | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": | ||
@@ -130,0 +132,0 @@ case "bytes": { |
@@ -1,3 +0,3 @@ | ||
export type UintType = number | bigint; | ||
export const isUintType = (x: any): x is UintType => | ||
export type NumType = number | bigint; | ||
export const isNumType = (x: any): x is NumType => | ||
typeof x === "number" || typeof x === "bigint"; | ||
@@ -8,7 +8,8 @@ | ||
export type PrimitiveType = UintType | BytesType; | ||
export type PrimitiveType = NumType | BytesType; | ||
export const isPrimitiveType = (x: any): x is PrimitiveType => | ||
isUintType(x) || isBytesType(x); | ||
isNumType(x) || isBytesType(x); | ||
export type BinaryLiterals = "uint" | "bytes" | "array" | "object" | "switch"; | ||
export type BinaryLiterals = "int" | "uint" | "bytes" | "array" | "object" | "switch"; | ||
export type Endianness = "little" | "big"; //default is always big | ||
@@ -26,3 +27,3 @@ //Why only a max value of 2**(6*8)? | ||
export type UintSizeToPrimitive<Size extends number> = | ||
export type NumSizeToPrimitive<Size extends number> = | ||
Size extends NumberSize ? number : bigint; | ||
@@ -54,11 +55,13 @@ | ||
//size: number of bytes used to encode the item | ||
interface UintLayoutItemBase<T extends UintType> extends LayoutItemBase<"uint"> { | ||
interface NumLayoutItemBase<T extends NumType, Signed extends Boolean> | ||
extends LayoutItemBase<Signed extends true ? "int" : "uint"> { | ||
size: T extends bigint ? number : NumberSize, | ||
endianness?: Endianness, //default is big | ||
}; | ||
export interface PrimitiveFixedUintLayoutItem<T extends UintType> | ||
extends UintLayoutItemBase<T>, PrimitiveFixedCustom<T> {}; | ||
interface PrimitiveFixedNumLayoutItem<T extends NumType, Signed extends Boolean> | ||
extends NumLayoutItemBase<T, Signed>, PrimitiveFixedCustom<T> {}; | ||
export interface OptionalToFromUintLayoutItem<T extends UintType> | ||
extends UintLayoutItemBase<T>, OptionalToFromCustom<T> {}; | ||
interface OptionalToFromNumLayoutItem<T extends NumType, Signed extends Boolean> | ||
extends NumLayoutItemBase<T, Signed>, OptionalToFromCustom<T> {}; | ||
@@ -82,2 +85,3 @@ export interface FixedPrimitiveBytesLayoutItem | ||
readonly lengthSize?: NumberSize, | ||
readonly lengthEndianness?: Endianness, //default is big | ||
readonly custom?: CustomConversion<BytesType, any>, | ||
@@ -88,2 +92,3 @@ }; | ||
readonly lengthSize?: NumberSize, | ||
readonly lengthEndianness?: Endianness, //default is big | ||
readonly arrayItem: LayoutItem, | ||
@@ -100,12 +105,16 @@ }; | ||
export interface SwitchLayoutItem extends LayoutItemBase<"switch"> { | ||
readonly idSize: NumberSize, | ||
readonly idTag?: string, | ||
readonly idSize: NumberSize, | ||
readonly idEndianness?: Endianness, //default is big | ||
readonly idLayoutPairs: readonly IdLayoutPair<PlainId>[] | readonly IdLayoutPair<ConversionId>[], | ||
} | ||
export type UintLayoutItem = | | ||
PrimitiveFixedUintLayoutItem<number> | | ||
OptionalToFromUintLayoutItem<number> | | ||
PrimitiveFixedUintLayoutItem<bigint> | | ||
OptionalToFromUintLayoutItem<bigint>; | ||
type NumLayoutItem<Signed extends boolean = boolean> = | ||
PrimitiveFixedNumLayoutItem<number, Signed> | | ||
OptionalToFromNumLayoutItem<number, Signed> | | ||
PrimitiveFixedNumLayoutItem<bigint, Signed> | | ||
OptionalToFromNumLayoutItem<bigint, Signed>; | ||
export type IntLayoutItem = NumLayoutItem<true>; | ||
export type UintLayoutItem = NumLayoutItem<false>; | ||
export type BytesLayoutItem = | | ||
@@ -117,3 +126,3 @@ FixedPrimitiveBytesLayoutItem | | ||
export type LayoutItem = | ||
UintLayoutItem | BytesLayoutItem | ArrayLayoutItem | ObjectLayoutItem | SwitchLayoutItem; | ||
IntLayoutItem | UintLayoutItem | BytesLayoutItem | ArrayLayoutItem | ObjectLayoutItem | SwitchLayoutItem; | ||
export type NamedLayoutItem = LayoutItem & { readonly name: string }; | ||
@@ -144,10 +153,10 @@ export type Layout = readonly NamedLayoutItem[]; | ||
export type LayoutItemToType<I extends LayoutItem> = | ||
[I] extends [UintLayoutItem] | ||
? I["custom"] extends UintType | ||
[I] extends [NumLayoutItem] | ||
? I["custom"] extends NumType | ||
? I["custom"] | ||
: I["custom"] extends CustomConversion<infer FromType extends UintType, infer ToType> | ||
: I["custom"] extends CustomConversion<infer FromType extends NumType, infer ToType> | ||
? ToType | ||
: I["custom"] extends FixedConversion<infer FromType extends UintType, infer ToType> | ||
: I["custom"] extends FixedConversion<infer FromType extends NumType, infer ToType> | ||
? ToType | ||
: UintSizeToPrimitive<I["size"]> | ||
: NumSizeToPrimitive<I["size"]> | ||
: [I] extends [BytesLayoutItem] | ||
@@ -154,0 +163,0 @@ ? I["custom"] extends CustomConversion<BytesType, infer ToType> |
import { | ||
Endianness, | ||
Layout, | ||
@@ -11,8 +12,8 @@ LayoutItem, | ||
CustomConversion, | ||
UintType, | ||
isUintType, | ||
NumType, | ||
isNumType, | ||
isBytesType, | ||
numberMaxSize, | ||
} from "./layout"; | ||
import { checkUint8ArrayDeeplyEqual, checkUint8ArraySize, checkUintEquals } from "./utils"; | ||
import { checkUint8ArrayDeeplyEqual, checkUint8ArraySize, checkNumEquals } from "./utils"; | ||
@@ -55,2 +56,3 @@ export function serializeLayout<const L extends Layout>( | ||
switch (item.binary) { | ||
case "int": | ||
case "uint": { | ||
@@ -107,22 +109,41 @@ return item.size; | ||
//Wormhole uses big endian by default for all uints | ||
//endianess can be easily added to UintLayout items if necessary | ||
export function serializeUint( | ||
//see numberMaxSize comment in layout.ts | ||
const maxAllowedNumberVal = 2**(numberMaxSize * 8); | ||
export function serializeNum( | ||
encoded: Uint8Array, | ||
offset: number, | ||
val: UintType, | ||
val: NumType, | ||
bytes: number, | ||
endianness: Endianness = "big", | ||
signed: boolean = false, | ||
): number { | ||
if (val < 0 || (typeof val === "number" && !Number.isInteger(val))) | ||
throw new Error(`Value ${val} is not an unsigned integer`); | ||
if (!signed && val < 0) | ||
throw new Error(`Value ${val} is negative but unsigned`); | ||
if (bytes > numberMaxSize && typeof val === "number" && val >= 2**(numberMaxSize * 8)) | ||
throw new Error(`Value ${val} is too large to be safely converted into an integer`); | ||
if (typeof val === "number") { | ||
if (!Number.isInteger(val)) | ||
throw new Error(`Value ${val} is not an integer`); | ||
if (val >= 2n ** BigInt(bytes * 8)) | ||
if (bytes > numberMaxSize) { | ||
if (val >= maxAllowedNumberVal) | ||
throw new Error(`Value ${val} is too large to be safely converted into an integer`); | ||
if (signed && val <= -maxAllowedNumberVal) | ||
throw new Error(`Value ${val} is too small to be safely converted into an integer`); | ||
} | ||
} | ||
const bound = 2n ** BigInt(bytes * 8) | ||
if (val >= bound) | ||
throw new Error(`Value ${val} is too large for ${bytes} bytes`); | ||
//big endian byte order | ||
if (signed && val < -bound) | ||
throw new Error(`Value ${val} is too small for ${bytes} bytes`); | ||
//correctly handles both signed and unsigned values | ||
for (let i = 0; i < bytes; ++i) | ||
encoded[offset + i] = Number((BigInt(val) >> BigInt(8*(bytes-i-1)) & 0xffn)); | ||
encoded[offset + i] = | ||
Number((BigInt(val) >> BigInt(8 * (endianness === "big" ? bytes-i-1 : i)) & 0xffn)); | ||
@@ -140,20 +161,21 @@ return offset + bytes; | ||
switch (item.binary) { | ||
case "switch": { | ||
const [idOrConversionId, layout] = findIdLayoutPair(item, data); | ||
const idNum = (Array.isArray(idOrConversionId) ? idOrConversionId[0] : idOrConversionId); | ||
offset = serializeUint(encoded, offset, idNum, item.idSize); | ||
offset = serializeLayout(layout, data, encoded, offset); | ||
break; | ||
} | ||
case "object": { | ||
offset = serializeLayout(item.layout, data, encoded, offset); | ||
break; | ||
} | ||
case "array": { | ||
if (item.lengthSize !== undefined) | ||
offset = serializeUint(encoded, offset, data.length, item.lengthSize); | ||
case "int": | ||
case "uint": { | ||
const value = (() => { | ||
if (isNumType(item.custom)) { | ||
if (!(item as { omit?: boolean })?.omit) | ||
checkNumEquals(item.custom, data); | ||
return item.custom; | ||
} | ||
for (let i = 0; i < data.length; ++i) | ||
offset = serializeLayoutItem(withIgnoredName(item.arrayItem), data[i], encoded, offset); | ||
if (isNumType(item?.custom?.from)) | ||
//no proper way to deeply check equality of item.custom.to and data in JS | ||
return item!.custom!.from; | ||
type narrowedCustom = CustomConversion<number, any> | CustomConversion<bigint, any>; | ||
return item.custom !== undefined ? (item.custom as narrowedCustom).from(data) : data; | ||
})(); | ||
offset = | ||
serializeNum(encoded, offset, value, item.size, item.endianness, item.binary === "int"); | ||
break; | ||
@@ -179,3 +201,4 @@ } | ||
else if (item.lengthSize !== undefined) | ||
offset = serializeUint(encoded, offset, ret.length, item.lengthSize); | ||
offset = | ||
serializeNum(encoded, offset, ret.length, item.lengthSize, item.lengthEndianness); | ||
@@ -189,21 +212,23 @@ return ret; | ||
} | ||
case "uint": { | ||
const value = (() => { | ||
if (isUintType(item.custom)) { | ||
if (!(item as { omit?: boolean })?.omit) | ||
checkUintEquals(item.custom, data); | ||
return item.custom; | ||
} | ||
case "array": { | ||
if (item.lengthSize !== undefined) | ||
offset = | ||
serializeNum(encoded, offset, data.length, item.lengthSize, item.lengthEndianness); | ||
if (isUintType(item?.custom?.from)) | ||
//no proper way to deeply check equality of item.custom.to and data in JS | ||
return item!.custom!.from; | ||
for (let i = 0; i < data.length; ++i) | ||
offset = serializeLayoutItem(withIgnoredName(item.arrayItem), data[i], encoded, offset); | ||
type narrowedCustom = CustomConversion<number, any> | CustomConversion<bigint, any>; | ||
return item.custom !== undefined ? (item.custom as narrowedCustom).from(data) : data; | ||
})(); | ||
offset = serializeUint(encoded, offset, value, item.size); | ||
break; | ||
} | ||
case "object": { | ||
offset = serializeLayout(item.layout, data, encoded, offset); | ||
break; | ||
} | ||
case "switch": { | ||
const [idOrConversionId, layout] = findIdLayoutPair(item, data); | ||
const idNum = (Array.isArray(idOrConversionId) ? idOrConversionId[0] : idOrConversionId); | ||
offset = serializeNum(encoded, offset, idNum, item.idSize, item.idEndianness); | ||
offset = serializeLayout(layout, data, encoded, offset); | ||
break; | ||
} | ||
} | ||
@@ -210,0 +235,0 @@ } |
@@ -8,3 +8,3 @@ export const checkUint8ArraySize = (custom: Uint8Array, size: number): void => { | ||
export const checkUintEquals = (custom: number | bigint, data: number | bigint): void => { | ||
export const checkNumEquals = (custom: number | bigint, data: number | bigint): void => { | ||
if (custom != data) | ||
@@ -11,0 +11,0 @@ throw new Error( |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1564181
18485