@wormhole-foundation/sdk-base 0.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,128 @@
1
+ import { ChainName } from "./chains";
2
+ import { Network } from "./networks";
3
+ import { RoArray, ToMapping, column, constMap } from "../utils";
4
+
5
+ const platformAndChainsEntries = [
6
+ [
7
+ "Evm",
8
+ [
9
+ "Ethereum",
10
+ "Bsc",
11
+ "Polygon",
12
+ "Avalanche",
13
+ "Oasis",
14
+ "Aurora",
15
+ "Fantom",
16
+ "Karura",
17
+ "Acala",
18
+ "Klaytn",
19
+ "Celo",
20
+ "Moonbeam",
21
+ "Neon",
22
+ "Arbitrum",
23
+ "Optimism",
24
+ "Gnosis",
25
+ "Base",
26
+ "Sepolia",
27
+ ],
28
+ ],
29
+ ["Solana", ["Solana", "Pythnet"]],
30
+ ["Cosmwasm", ["Terra", "Terra2", "Injective", "Xpla", "Sei"]],
31
+ ["Btc", ["Btc"]],
32
+ ["Algorand", ["Algorand"]],
33
+ ["Sui", ["Sui"]],
34
+ ["Aptos", ["Aptos"]],
35
+ ["Osmosis", ["Osmosis"]],
36
+ ["Wormchain", ["Wormchain"]],
37
+ ["Near", ["Near"]],
38
+ ] as const satisfies RoArray<readonly [string, RoArray<ChainName>]>;
39
+
40
+ export const platforms = column(platformAndChainsEntries, 0);
41
+ export type PlatformName = (typeof platforms)[number];
42
+
43
+ export const platformToChains = constMap(platformAndChainsEntries);
44
+ export const chainToPlatform = constMap(platformAndChainsEntries, [1, 0]);
45
+
46
+ export const isPlatform = (platform: string): platform is PlatformName =>
47
+ platformToChains.has(platform);
48
+
49
+ export type PlatformToChains<P extends PlatformName> = ReturnType<
50
+ typeof platformToChains<P>
51
+ >[number];
52
+ export type ChainToPlatform<C extends ChainName> = ReturnType<
53
+ typeof chainToPlatform<C>
54
+ >;
55
+
56
+ const networkChainEvmCIdEntries = [
57
+ [
58
+ "Mainnet",
59
+ [
60
+ ["Ethereum", 1n],
61
+ // TODO: forced to add this to match other list
62
+ ["Sepolia", 0n],
63
+ ["Bsc", 56n],
64
+ ["Polygon", 137n],
65
+ ["Avalanche", 43114n],
66
+ ["Oasis", 42262n],
67
+ ["Aurora", 1313161554n],
68
+ ["Fantom", 250n],
69
+ ["Karura", 686n],
70
+ ["Acala", 787n],
71
+ ["Klaytn", 8217n],
72
+ ["Celo", 42220n],
73
+ ["Moonbeam", 1284n],
74
+ ["Neon", 245022934n],
75
+ ["Arbitrum", 42161n],
76
+ ["Optimism", 10n],
77
+ ["Gnosis", 100n],
78
+ ["Base", 8453n],
79
+ ],
80
+ ],
81
+ [
82
+ "Testnet",
83
+ [
84
+ ["Ethereum", 5n], //goerli
85
+ ["Sepolia", 11155111n], //actually just another ethereum testnet...
86
+ ["Bsc", 97n],
87
+ ["Polygon", 80001n], //mumbai
88
+ ["Avalanche", 43113n], //fuji
89
+ ["Oasis", 42261n],
90
+ ["Aurora", 1313161555n],
91
+ ["Fantom", 4002n],
92
+ ["Karura", 596n],
93
+ ["Acala", 597n],
94
+ ["Klaytn", 1001n], //baobab
95
+ ["Celo", 44787n], //alfajores
96
+ ["Moonbeam", 1287n], //moonbase alpha
97
+ ["Neon", 245022940n],
98
+ ["Arbitrum", 421613n], //arbitrum goerli
99
+ ["Optimism", 420n],
100
+ ["Gnosis", 77n],
101
+ ["Base", 84531n],
102
+ ],
103
+ ],
104
+ ] as const satisfies RoArray<
105
+ readonly [Network, RoArray<readonly [PlatformToChains<"Evm">, bigint]>]
106
+ >;
107
+
108
+ export const evmChainIdToNetworkChainPair = constMap(
109
+ networkChainEvmCIdEntries,
110
+ [2, [0, 1]]
111
+ );
112
+ export const evmNetworkChainToEvmChainId = constMap(networkChainEvmCIdEntries);
113
+
114
+ const networkChainSolanaGenesisHashes = [
115
+ ["Mainnet", [["Solana", "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d"]]],
116
+ ["Testnet", [["Solana", "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG"]]], // Note: this is referred to as `devnet` in sol
117
+ ] as const satisfies RoArray<
118
+ readonly [Network, RoArray<readonly [ChainName, string]>]
119
+ >;
120
+
121
+ export const solGenesisHashToNetworkChainPair = constMap(
122
+ networkChainSolanaGenesisHashes,
123
+ [2, [0, 1]]
124
+ );
125
+
126
+ export const solNetworkChainToGenesisHash = constMap(
127
+ networkChainSolanaGenesisHashes
128
+ );
@@ -0,0 +1,36 @@
1
+ /* TODO:
2
+ * governance actions have a module parameter:
3
+ * - "Core" - https://github.com/wormhole-foundation/wormhole/blob/9e61d151c61bedb18ab1d4ca6ffb1c6c91b108f0/ethereum/contracts/Governance.sol#L21
4
+ * - "TokenBridge" - https://github.com/wormhole-foundation/wormhole/blob/9e61d151c61bedb18ab1d4ca6ffb1c6c91b108f0/ethereum/contracts/bridge/BridgeGovernance.sol#L24
5
+ * - "NFTBridge" - https://github.com/wormhole-foundation/wormhole/blob/9e61d151c61bedb18ab1d4ca6ffb1c6c91b108f0/ethereum/contracts/nft/NFTBridgeGovernance.sol#L23
6
+ * - "WormholeRelayer" - https://github.com/wormhole-foundation/wormhole/blob/9e61d151c61bedb18ab1d4ca6ffb1c6c91b108f0/ethereum/contracts/relayer/wormholeRelayer/WormholeRelayerGovernance.sol#L43
7
+ * while the core contract is actually called "Wormhole" in most platforms
8
+ * - though in solana it's called bridge - https://github.com/wormhole-foundation/wormhole/tree/main/solana/bridge
9
+ * - and in algorand it seems to be called wormhole_core - https://github.com/wormhole-foundation/wormhole/blob/main/algorand/wormhole_core.py
10
+ * - and in EVM it resides directly in the contracts directory
11
+ * the naming of the token bridge and the nft bridge seem to be a lot more consistent, though:
12
+ * - for EVM the TokenBridge and NFTbridge reside in the "bridge" and "nft" directory respectively: https://github.com/wormhole-foundation/wormhole/tree/main/ethereum/contracts)
13
+ * the WormholeRelayer resides in relayer/wormholeRelayer (only built for EVM so far)
14
+ *
15
+ * Within the solana directory, only the token bridge and the nft bridge are considered modules
16
+ * (i.e. are in the modules directory: https://github.com/wormhole-foundation/wormhole/tree/main/solana/modules).
17
+ * While in the JS SDK, the core bridge functionality is in the "bridge" directory
18
+ * (notice the clash with EVM where bridge refers to the token bridge...).
19
+ *
20
+ * With all of this in mind: What should we name modules here?
21
+ * My preferred choice would be ["CoreBridge", "TokenBridge", "NftBridge", "Relayer"]
22
+ * but ["Core", "TokenBridge", "NFTBridge", "WormholeRelayer"] seems to be more consistent given
23
+ * current naming "conventions"
24
+ */
25
+
26
+ export const protocols = [
27
+ "CoreBridge",
28
+ "TokenBridge",
29
+ "NftBridge",
30
+ "Relayer",
31
+ "CCTP",
32
+ ] as const;
33
+
34
+ export type ProtocolName = (typeof protocols)[number];
35
+ export const isProtocolName = (protocol: string): protocol is ProtocolName =>
36
+ protocols.includes(protocol as ProtocolName);
@@ -0,0 +1,48 @@
1
+ import { constMap, RoArray } from "../utils";
2
+ import { Network } from "./networks";
3
+ import { ChainName } from "./chains";
4
+
5
+ const rpcConfig = [
6
+ [
7
+ "Mainnet",
8
+ [
9
+ ["Ethereum", "https://rpc.ankr.com/eth"],
10
+ ["Solana", "https://api.mainnet-beta.solana.com"],
11
+ ["Polygon", "https://rpc.ankr.com/polygon"],
12
+ ["Bsc", "https://bscrpc.com"],
13
+ ["Avalanche", "https://rpc.ankr.com/avalanche"],
14
+ ["Fantom", "https://rpc.ankr.com/fantom"],
15
+ ["Celo", "https://rpc.ankr.com/celo"],
16
+ ["Moonbeam", "https://rpc.ankr.com/moonbeam"],
17
+ ["Sui", "https://rpc.mainnet.sui.io"],
18
+ ["Aptos", "https://fullnode.mainnet.aptoslabs.com/v1"],
19
+ ["Arbitrum", "https://arb1.arbitrum.io/rpc"],
20
+ ["Optimism", "https://mainnet.optimism.io"],
21
+ ["Sei", ""], // TODO
22
+ ],
23
+ ],
24
+ [
25
+ "Testnet",
26
+ [
27
+ ["Ethereum", "https://rpc.ankr.com/eth_goerli"],
28
+ ["Polygon", "https://polygon-mumbai.blockpi.network/v1/rpc/public"],
29
+ ["Bsc", "https://data-seed-prebsc-1-s3.binance.org:8545"],
30
+ ["Avalanche", "https://api.avax-test.network/ext/bc/C/rpc"],
31
+ ["Fantom", "https://rpc.ankr.com/fantom_testnet"],
32
+ ["Celo", "https://alfajores-forno.celo-testnet.org"],
33
+ ["Solana", "https://api.devnet.solana.com"],
34
+ ["Moonbeam", "https://rpc.api.moonbase.moonbeam.network"],
35
+ ["Sui", "https://fullnode.testnet.sui.io"],
36
+ ["Aptos", "https://fullnode.testnet.aptoslabs.com/v1"],
37
+ ["Sei", "https://rpc.atlantic-2.seinetwork.io"],
38
+ ["Arbitrum", "https://arbitrum-goerli.publicnode.com"],
39
+ ["Optimism", "https://optimism-goerli.publicnode.com"],
40
+ ],
41
+ ],
42
+ ] as const satisfies RoArray<
43
+ readonly ["Mainnet" | "Testnet", RoArray<readonly [ChainName, string]>]
44
+ >;
45
+
46
+ const rpc = constMap(rpcConfig);
47
+ export const rpcAddress = (network: Network, chain: ChainName) =>
48
+ network === "Devnet" ? undefined : rpc.get(network, chain);
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./constants";
2
+ export * from "./utils";
@@ -0,0 +1,102 @@
1
+ import { ParseNumber, RoArray, RoArray2D } from "./metaprogramming";
2
+
3
+
4
+ //TODO the intent here is that number represents a number literal, but strictly speaking
5
+ // the type allows for unions of number literals (and an array of such unions)
6
+ export type IndexEs = number | RoArray<number>;
7
+
8
+ export const range = (length: number) => [...Array(length).keys()];
9
+
10
+ export type Entries<T extends RoArray> =
11
+ readonly [...{ [K in keyof T]: K extends `${number}` ? [T[K], ParseNumber<K>] : never }];
12
+
13
+ export type Flatten<T extends RoArray> =
14
+ T extends readonly [infer Head, ...infer Tail]
15
+ ? Head extends RoArray
16
+ ? [...Head, ...Flatten<Tail>]
17
+ : [Head, ...Flatten<Tail>]
18
+ : [];
19
+
20
+ export type InnerFlatten<T extends RoArray> =
21
+ [...{ [K in keyof T]:
22
+ K extends `${number}`
23
+ ? T[K] extends RoArray
24
+ ? Flatten<T[K]>
25
+ : T[K]
26
+ : never
27
+ }];
28
+
29
+ export type IsFlat<T extends RoArray> =
30
+ T extends readonly [infer Head, ...infer Tail]
31
+ ? Head extends RoArray
32
+ ? false
33
+ : IsFlat<Tail>
34
+ : true;
35
+
36
+ export type Unflatten<T extends RoArray> =
37
+ [...{ [K in keyof T]: K extends `${number}` ? readonly [T[K]] : never }];
38
+
39
+ export type AllSameLength<T extends RoArray2D, L extends number> =
40
+ T extends readonly [infer Head extends RoArray, ...infer Tail extends RoArray2D]
41
+ ? Head["length"] extends L
42
+ ? AllSameLength<Tail, L>
43
+ : false
44
+ : true;
45
+
46
+ export type IsRectangular<T extends RoArray> =
47
+ T extends RoArray2D
48
+ ? T extends readonly [infer Head extends RoArray, ...infer Tail extends RoArray2D]
49
+ ? AllSameLength<Tail, Head["length"]>
50
+ : true //empty array is rectangular
51
+ : IsFlat<T>; //1d array is rectangular
52
+
53
+ export type Column<A extends RoArray2D, I extends number> =
54
+ [...{ [K in keyof A]: K extends `${number}` ? A[K][I] : never }];
55
+
56
+ export const column = <A extends RoArray2D, I extends number>(tupArr: A, index: I) =>
57
+ tupArr.map((tuple) => tuple[index]) as Column<A, I>;
58
+
59
+ export type Zip<A extends RoArray2D> =
60
+ //TODO remove, find max length, and return undefined for elements in shorter arrays
61
+ IsRectangular<A> extends true
62
+ ? A[0] extends infer Head extends RoArray
63
+ ? [...{ [K in keyof Head]:
64
+ K extends `${number}`
65
+ ? readonly [...{ [K2 in keyof A]: K extends keyof A[K2] ? A[K2][K] : never }]
66
+ : never
67
+ }]
68
+ : []
69
+ : never
70
+
71
+ export const zip = <const Args extends RoArray2D>(arr: Args) =>
72
+ range(arr[0].length).map(col =>
73
+ range(arr.length).map(row => arr[row][col])
74
+ ) as unknown as ([Zip<Args>] extends [never] ? RoArray2D : Zip<Args>);
75
+
76
+ export type OnlyIndexes<E extends RoArray, I extends IndexEs> =
77
+ I extends number
78
+ ? OnlyIndexes<E, [I]>
79
+ : I extends readonly [infer Head extends number, ...infer Tail extends RoArray<number>]
80
+ ? E[Head] extends undefined
81
+ ? OnlyIndexes<E, Tail>
82
+ : [E[Head], ...OnlyIndexes<E, Tail>]
83
+ : [];
84
+
85
+ type ExcludeIndexesImpl<T extends RoArray, C extends number> =
86
+ T extends readonly [infer Head, ...infer Tail]
87
+ ? Head extends readonly [infer V, infer I extends number]
88
+ ? I extends C
89
+ ? ExcludeIndexesImpl<Tail, C>
90
+ : [V, ...ExcludeIndexesImpl<Tail, C>]
91
+ : never
92
+ : [];
93
+
94
+ export type ExcludeIndexes<T extends RoArray, C extends IndexEs> =
95
+ ExcludeIndexesImpl<Entries<T>, C extends RoArray<number> ? ParseNumber<keyof IndexEs> : C>;
96
+
97
+ export type Cartesian<L, R> =
98
+ L extends RoArray
99
+ ? Flatten<[...{ [K in keyof L]: K extends `${number}` ? Cartesian<L[K], R> : never }]>
100
+ : R extends RoArray
101
+ ? [...{ [K in keyof R]: K extends `${number}` ? readonly [L, R[K]] : never }]
102
+ : [L, R];
@@ -0,0 +1,62 @@
1
+ export const stripPrefix = (prefix: string, str: string): string =>
2
+ str.startsWith(prefix) ? str.slice(prefix.length) : str;
3
+
4
+ const tryAsciiHexCharToNumber = (str: string, index: number): number => {
5
+ //we could use parseInt(char, 16) but given that we are doing this for every single char and
6
+ // parseInt has to check for optional "0x" prefix and other conversion stuff, let's just be
7
+ // explicit and not overly wasteful with performance
8
+ const charCode = str.charCodeAt(index);
9
+ switch (charCode) {
10
+ case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57:
11
+ return charCode - 48; //ascii 0-9 is 48-57
12
+ case 65: case 66: case 67: case 68: case 69: case 70:
13
+ return charCode - 65 + 10; //ascii A-F is 65-70
14
+ case 97: case 98: case 99: case 100: case 101: case 102:
15
+ return charCode - 97 + 10; //ascii a-f is 97-102
16
+ default:
17
+ return Number.NaN;
18
+ }
19
+ }
20
+
21
+ const asciiHexCharToNumber = (str: string, index: number): number => {
22
+ const val = tryAsciiHexCharToNumber(str, index);
23
+ if (Number.isNaN(val))
24
+ throw new Error(
25
+ `Character ${str.charAt(index)} at position ${index} in ${str} is not a hex char`
26
+ );
27
+
28
+ return val;
29
+ }
30
+
31
+ export const isHexByteString = (str: string, expectedBytes?: number): boolean => {
32
+ let i = ((str.length > 1 && str[1] == "x") ? 2 : 0);
33
+ if ((str.length % 2 !== 0) ||
34
+ (expectedBytes !== undefined && str.length - i !== 2 * expectedBytes))
35
+ return false;
36
+
37
+ for (; i < str.length; ++i)
38
+ if (Number.isNaN(tryAsciiHexCharToNumber(str, i)))
39
+ return false;
40
+
41
+ return true;
42
+ }
43
+
44
+ //TODO naming: arrayify (ethers), toBytes (solana)
45
+ export const hexByteStringToUint8Array = (str: string): Uint8Array => {
46
+ if (str.length % 2 !== 0)
47
+ throw new Error(`hex byte string has odd length: ${str}`);
48
+
49
+ const prefixOffset = str.length > 2 && str[1] === "x" ? 2 : 0;
50
+ const ret = new Uint8Array((str.length - prefixOffset) / 2);
51
+ for (let i = prefixOffset; i < str.length; i += 2)
52
+ ret[(i - prefixOffset) / 2] =
53
+ asciiHexCharToNumber(str, i) * 16 +
54
+ asciiHexCharToNumber(str, i + 1);
55
+
56
+ return ret;
57
+ }
58
+
59
+ //TODO naming: hexlify (ethers)
60
+ export const uint8ArrayToHexByteString = (arr: Uint8Array, addPrefix = true): string =>
61
+ (addPrefix ? "0x" : "") +
62
+ Array.from(arr).map(byte => byte.toString(16).padStart(2, "0")).join("");
@@ -0,0 +1,5 @@
1
+ export * from "./array";
2
+ export * from "./hexstring";
3
+ export * from "./layout";
4
+ export * from "./mapping";
5
+ export * from "./metaprogramming";
@@ -0,0 +1,177 @@
1
+ import {
2
+ Layout,
3
+ LayoutItem,
4
+ LayoutToType,
5
+ LayoutItemToType,
6
+ FixedPrimitiveBytesLayoutItem,
7
+ FixedValueBytesLayoutItem,
8
+ CustomConversion,
9
+ UintSizeToPrimitive,
10
+ numberMaxSize,
11
+ } from "./layout";
12
+
13
+ import { checkUint8ArrayDeeplyEqual, checkUintEquals } from "./utils";
14
+
15
+ export function deserializeLayout<const L extends Layout>(
16
+ layout: L,
17
+ encoded: Uint8Array,
18
+ offset?: number,
19
+ consumeAll?: true,
20
+ ): LayoutToType<L>;
21
+
22
+ export function deserializeLayout<const L extends Layout>(
23
+ layout: L,
24
+ encoded: Uint8Array,
25
+ offset?: number,
26
+ consumeAll?: false,
27
+ ): readonly [LayoutToType<L>, number];
28
+
29
+ export function deserializeLayout<const L extends Layout>(
30
+ layout: L,
31
+ encoded: Uint8Array,
32
+ offset = 0,
33
+ consumeAll = true,
34
+ ): LayoutToType<L> | readonly [LayoutToType<L>, number] {
35
+ const [decoded, finalOffset] = internalDeserializeLayout(layout, encoded, offset);
36
+
37
+ if (consumeAll && finalOffset !== encoded.length)
38
+ throw new Error(`encoded data is longer than expected: ${encoded.length} > ${finalOffset}`);
39
+
40
+ return consumeAll ? decoded as LayoutToType<L> : [decoded as LayoutToType<L>, finalOffset];
41
+ }
42
+
43
+ function internalDeserializeLayout(
44
+ layout: Layout,
45
+ encoded: Uint8Array,
46
+ offset: number,
47
+ ): [any, number] {
48
+ let decoded = {} as any;
49
+ for (const item of layout)
50
+ [((item as any).omit ? {} : decoded)[item.name], offset] =
51
+ deserializeLayoutItem(item, encoded, offset);
52
+
53
+ return [decoded, offset];
54
+ }
55
+
56
+ function updateOffset (
57
+ encoded: Uint8Array,
58
+ offset: number,
59
+ size: number
60
+ ): number {
61
+ const newOffset = offset + size;
62
+ if (newOffset > encoded.length)
63
+ throw new Error(`encoded data is shorter than expected: ${encoded.length} < ${newOffset}`);
64
+
65
+ return newOffset;
66
+ }
67
+
68
+ function deserializeUint<S extends number>(
69
+ encoded: Uint8Array,
70
+ offset: number,
71
+ size: S,
72
+ ): readonly [UintSizeToPrimitive<S>, number] {
73
+ let value = 0n;
74
+ for (let i = 0; i < size; ++i)
75
+ value += BigInt(encoded[offset + i]) << BigInt((size - 1 - i) * 8);
76
+
77
+ return [
78
+ ((size > numberMaxSize) ? value : Number(value)) as UintSizeToPrimitive<S>,
79
+ updateOffset(encoded, offset, size)
80
+ ] as const;
81
+ }
82
+
83
+ function deserializeLayoutItem(
84
+ item: LayoutItem,
85
+ encoded: Uint8Array,
86
+ offset: number,
87
+ ): readonly [any, number] {
88
+ try {
89
+ switch (item.binary) {
90
+ case "object": {
91
+ return internalDeserializeLayout(item.layout, encoded, offset);
92
+ }
93
+ case "array": {
94
+ let ret = [] as LayoutToType<typeof item.layout>[];
95
+ if (item.lengthSize !== undefined) {
96
+ const [length, newOffset] = deserializeUint(encoded, offset, item.lengthSize);
97
+ offset = newOffset;
98
+ for (let i = 0; i < length; ++i)
99
+ [ret[i], offset] = internalDeserializeLayout(item.layout, encoded, offset);
100
+ }
101
+ else {
102
+ while (offset < encoded.length)
103
+ [ret[ret.length], offset] = internalDeserializeLayout(item.layout, encoded, offset);
104
+ }
105
+ return [ret, offset];
106
+ }
107
+ case "bytes": {
108
+ let newOffset;
109
+ let fixedFrom;
110
+ let fixedTo;
111
+ if (item.custom !== undefined) {
112
+ if (item.custom instanceof Uint8Array)
113
+ fixedFrom = item.custom;
114
+ else if (item.custom.from instanceof Uint8Array) {
115
+ fixedFrom = item.custom.from;
116
+ fixedTo = item.custom.to;
117
+ }
118
+ }
119
+
120
+ if (fixedFrom !== undefined)
121
+ newOffset = updateOffset(encoded, offset, fixedFrom.length);
122
+ else {
123
+ item = item as
124
+ Exclude<typeof item, FixedPrimitiveBytesLayoutItem | FixedValueBytesLayoutItem>;
125
+ if ("size" in item && item.size !== undefined)
126
+ newOffset = updateOffset(encoded, offset, item.size);
127
+ else if (item.lengthSize !== undefined) {
128
+ let length;
129
+ [length, offset] = deserializeUint(encoded, offset, item.lengthSize);
130
+ newOffset = updateOffset(encoded, offset, length);
131
+ }
132
+ else
133
+ newOffset = encoded.length;
134
+ }
135
+
136
+ const value = encoded.slice(offset, newOffset);
137
+ if (fixedFrom !== undefined) {
138
+ checkUint8ArrayDeeplyEqual(fixedFrom, value);
139
+ return [fixedTo ?? fixedFrom, newOffset];
140
+ }
141
+
142
+ type narrowedCustom = CustomConversion<Uint8Array, any>;
143
+ return [
144
+ item.custom !== undefined ? (item.custom as narrowedCustom).to(value) : value,
145
+ newOffset
146
+ ];
147
+ }
148
+ case "uint": {
149
+ const [value, newOffset] = deserializeUint(encoded, offset, item.size);
150
+
151
+ if (item.custom !== undefined) {
152
+ if (typeof item.custom === "number" || typeof item.custom === "bigint") {
153
+ checkUintEquals(item.custom, value);
154
+ return [item.custom, newOffset];
155
+ }
156
+ else if (typeof item.custom.from === "number" || typeof item.custom.from === "bigint") {
157
+ checkUintEquals(item.custom.from, value);
158
+ return [item.custom.to, newOffset];
159
+ }
160
+ }
161
+
162
+ //narrowing to CustomConver<number | bigint, any> is a bit hacky here, since the true type
163
+ // would be CustomConver<number, any> | CustomConver<bigint, any>, but then we'd have to
164
+ // further tease that apart still for no real gain...
165
+ type narrowedCustom = CustomConversion<number | bigint, any>;
166
+ return [
167
+ item.custom !== undefined ? (item.custom as narrowedCustom).to(value) : value,
168
+ newOffset
169
+ ];
170
+ }
171
+ }
172
+ }
173
+ catch (e) {
174
+ (e as Error).message = `when deserializing item '${item.name}': ${(e as Error).message}`;
175
+ throw e;
176
+ }
177
+ }