@epoch-protocol/epoch-commons-sdk 0.1.20 → 0.1.21-mainnet.0

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.
@@ -97,10 +97,13 @@ export const ARBITER_ADDRESS = {
97
97
  10: "0x9f1de3ca45b09c61792a6f649ae68f420aa8a43b",
98
98
  42161: "0x9f1de3ca45b09c61792a6f649ae68f420aa8a43b",
99
99
  8453: "0x9f1de3ca45b09c61792a6f649ae68f420aa8a43b",
100
+ // Robinhood placeholders — replace after deployment.
101
+ 4663: "0x9f1de3ca45b09c61792a6f649ae68f420aa8a43b",
100
102
  // Testnets
101
103
  11155111: "0x2F5eA866716A04e91a55099620F2e1B423eC7011",
102
104
  84532: "0x2F5eA866716A04e91a55099620F2e1B423eC7011",
103
105
  11155420: "0x2F5eA866716A04e91a55099620F2e1B423eC7011",
106
+ 46630: "0x2F5eA866716A04e91a55099620F2e1B423eC7011",
104
107
  123456789: "0x9f1de3ca45b09c61792a6f649ae68f420aa8a43b",
105
108
  999999999: "0x9f1de3ca45b09c61792a6f649ae68f420aa8a43b",
106
109
  };
@@ -111,10 +114,13 @@ export const ALLOCATOR_ADDRESS = {
111
114
  10: "0x444E8C0fC85BB47aBD3E97d00E2E2B0719034769",
112
115
  42161: "0x444E8C0fC85BB47aBD3E97d00E2E2B0719034769",
113
116
  8453: "0x444E8C0fC85BB47aBD3E97d00E2E2B0719034769",
117
+ // Robinhood placeholders — replace after deployment.
118
+ 4663: "0x444E8C0fC85BB47aBD3E97d00E2E2B0719034769",
114
119
  // Testnets
115
120
  11155111: "0xa46827635A7d329608085401a3b2Bf14a8a2bDF9",
116
121
  84532: "0xa46827635A7d329608085401a3b2Bf14a8a2bDF9",
117
122
  11155420: "0xa46827635A7d329608085401a3b2Bf14a8a2bDF9",
123
+ 46630: "0xa46827635A7d329608085401a3b2Bf14a8a2bDF9",
118
124
  123456789: "0x444E8C0fC85BB47aBD3E97d00E2E2B0719034769",
119
125
  999999999: "0x444E8C0fC85BB47aBD3E97d00E2E2B0719034769",
120
126
  };
@@ -134,6 +140,32 @@ export function getArbiterAddress(chainId) {
134
140
  }
135
141
  return address;
136
142
  }
143
+ // The Robinhood Testnet deployment uses the same deterministic/testnet
144
+ // addresses as the existing Epoch testnet configuration. Populate every
145
+ // shared contract map together so any consumer consulting one of these maps
146
+ // cannot silently treat 46630 as unsupported.
147
+ const TESTNET_CONTRACT_ADDRESS_MAPS = [
148
+ INTENT_REGISTRY_ADDRESS,
149
+ EPOCH_MODULE_SAFE_ADDRESS,
150
+ EPOCH_SAFE_INIT_SETUP,
151
+ SAFE_INIT_SETUP_FORWARDER,
152
+ SAFE_WEBAUTHN_SHARED_SIGNER,
153
+ SAFE_WEBAUTHN_SIGNER_FACTORY,
154
+ FCLP256Verifier,
155
+ SAFE_PROXY_FACTORY_ADDRESS,
156
+ SAFE_SINGLETON_ADDRESS,
157
+ SAFE_7702_SINGLETON_ADDRESS,
158
+ SAFE_7579_MODULE_ADDRESS,
159
+ SAFE_7579_REGISTRY_ADDRESS,
160
+ EIP7702_SAFE_PROXY_ADDRESS,
161
+ METAMASK_EXEC_MANAGER,
162
+ EPOCH_ESCROW_ADDRESS,
163
+ SAFE_MULTISEND_ADDRESS,
164
+ SAFE_MULTISEND_CALL_ONLY_ADDRESS,
165
+ ];
166
+ for (const addresses of TESTNET_CONTRACT_ADDRESS_MAPS) {
167
+ addresses[46630] = addresses[11155420];
168
+ }
137
169
  const CONTRACT_ADDRESSES = {
138
170
  METAMASK_EXEC_MANAGER,
139
171
  SAFE_PROXY_FACTORY_ADDRESS,
@@ -7,7 +7,7 @@ export declare const DUMMY_LENDING_PROTOCOL: "dummy-lending";
7
7
  export declare const DUMMY_LENDING_MARKET_ADDRESS = "0xa73c1ae0e4e9116020dba0a419f91585d73459ee";
8
8
  /** CREATE2 staking contract — same on all dummy-staking testnets. */
9
9
  export declare const DUMMY_STAKING_CONTRACT_ADDRESS = "0xA25A981bc027E1f13aC70882a323B8B8EE880B9C";
10
- export declare const DUMMY_LENDING_CHAIN_IDS: readonly [84532, 11155111, 11155420];
10
+ export declare const DUMMY_LENDING_CHAIN_IDS: readonly [84532, 11155111, 11155420, 46630];
11
11
  export type DummyLendingChainId = (typeof DUMMY_LENDING_CHAIN_IDS)[number];
12
12
  /** Unified CREATE2 token addresses — same on every dummy-lending chain. */
13
13
  export declare const DUMMY_LENDING_TOKENS: {
@@ -11,6 +11,7 @@ export const DUMMY_LENDING_CHAIN_IDS = [
11
11
  84532, // Base Sepolia
12
12
  11155111, // Ethereum Sepolia
13
13
  11155420, // Optimism Sepolia
14
+ 46630, // Robinhood Testnet
14
15
  ];
15
16
  /** Unified CREATE2 token addresses — same on every dummy-lending chain. */
16
17
  export const DUMMY_LENDING_TOKENS = {
@@ -51,9 +51,41 @@
51
51
  "*": "*"
52
52
  },
53
53
  "type": ["swapandbridge"]
54
+ },
55
+ "MidenBridge": {
56
+ "contractAddress": {
57
+ "*": "*"
58
+ },
59
+ "type": ["swapandbridge", "bridge"]
60
+ }
61
+ },
62
+ "oracles": {
63
+ "PragmaMidenV1": {
64
+ "chain": "Miden",
65
+ "oracleAccount": "0x8f8d747477ca171144feadc60a1137",
66
+ "rpcUrl": "https://rpc.cedar.miden.io",
67
+ "method": "get_median",
68
+ "defaultMaxAgeSeconds": 120,
69
+ "minimumPublishers": 1,
70
+ "feeds": {
71
+ "USDC/USD": {
72
+ "key": "0x1dfbb6c4acb913805c4df6731bfe7bfb8b6acd999bb768feb24979170807ee51",
73
+ "expectedDecimals": 6
74
+ },
75
+ "ETH/USD": {
76
+ "key": "0x9a50f4137c634e0b384a87bea3a186e578e21274af6ae752230ead4631233e1d",
77
+ "expectedDecimals": 8
78
+ }
79
+ }
54
80
  }
55
81
  },
56
82
  "tokens": {
83
+ "USDCX": {
84
+ "contractAddress": {
85
+ "Miden": "mm1arnfum9xuwwlrvgvrefejwvlavn3vk24_qr7qqq9wr6w"
86
+ },
87
+ "decimals": 6
88
+ },
57
89
  "DAI": {
58
90
  "contractAddress": {
59
91
  "Ethereum": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
@@ -80,7 +112,21 @@
80
112
  "Base": "0x4200000000000000000000000000000000000006",
81
113
  "Optimism": "0x4200000000000000000000000000000000000006",
82
114
  "Polygon": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
83
- "Arbitrum": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
115
+ "Arbitrum": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
116
+ "Robinhood": "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73",
117
+ "Miden": "mm1apqelr6tlaeftyfj4f7w59dycysmtawe_qr7qqq9wr6w"
118
+ },
119
+ "decimals": 18
120
+ },
121
+ "USDG": {
122
+ "contractAddress": {
123
+ "Robinhood": "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168"
124
+ },
125
+ "decimals": 6
126
+ },
127
+ "USDe": {
128
+ "contractAddress": {
129
+ "Robinhood": "0x5d3a1Ff2b6BAb83B63cd9AD0787074081a52ef34"
84
130
  },
85
131
  "decimals": 18
86
132
  },
@@ -114,6 +160,14 @@
114
160
  "Arbitrum": {
115
161
  "chainId": 42161,
116
162
  "explorer": "https://arbiscan.io"
163
+ },
164
+ "Robinhood": {
165
+ "chainId": 4663,
166
+ "explorer": "https://robinhoodchain.blockscout.com"
167
+ },
168
+ "Miden": {
169
+ "chainId": 999999999,
170
+ "explorer": "https://midenscan.com"
117
171
  }
118
172
  },
119
173
  "relationships": {
@@ -164,17 +218,32 @@
164
218
  "*": {
165
219
  "tokensInAndOut": ["*"]
166
220
  }
221
+ },
222
+ "MidenBridge": {
223
+ "*": {
224
+ "tokensInAndOut": ["*"]
225
+ }
167
226
  }
168
227
  },
169
228
  "tokenChains": {
170
229
  "DAI": ["Ethereum", "Base", "Optimism", "Polygon", "Arbitrum"],
171
230
  "USDC": ["Ethereum", "Base", "Optimism", "Polygon", "Arbitrum"],
172
- "WETH": ["Ethereum", "Base", "Optimism", "Polygon", "Arbitrum"],
231
+ "USDCX": ["Miden"],
232
+ "WETH": [
233
+ "Ethereum",
234
+ "Base",
235
+ "Optimism",
236
+ "Polygon",
237
+ "Arbitrum",
238
+ "Robinhood",
239
+ "Miden"
240
+ ],
241
+ "USDG": ["Robinhood"],
173
242
  "USDT": ["Ethereum", "Optimism", "Polygon", "Arbitrum"],
174
243
  "WBTC": ["Ethereum", "Optimism", "Polygon", "Arbitrum"],
175
244
  "wstETH": ["Ethereum", "Base", "Optimism", "Arbitrum"],
176
245
  "GHO": ["Ethereum", "Arbitrum", "Base"],
177
- "USDe": ["Ethereum", "Arbitrum"],
246
+ "USDe": ["Ethereum", "Arbitrum", "Robinhood"],
178
247
  "cbBTC": ["Ethereum", "Base"],
179
248
  "PYUSD": ["Ethereum"],
180
249
  "LINK": ["Ethereum", "Base", "Optimism", "Polygon", "Arbitrum"],
@@ -66,12 +66,19 @@
66
66
  }
67
67
  },
68
68
  "tokens": {
69
+ "USDCX": {
70
+ "contractAddress": {
71
+ "Miden": "0x4cbdcaffe75f0a317482224dae6436"
72
+ },
73
+ "decimals": 6
74
+ },
69
75
  "USDC": {
70
76
  "contractAddress": {
71
77
  "Base": "0x2BB4FfD7E2c6D432b697554Efd77fA13bdbefd69",
72
78
  "Mainnet": "0x2BB4FfD7E2c6D432b697554Efd77fA13bdbefd69",
73
- "Miden": "0x537c15a622074e91188aa894456c52",
74
- "Optimism": "0x2BB4FfD7E2c6D432b697554Efd77fA13bdbefd69"
79
+ "Miden": "0x5b043dcb48d5721106f7a1ceb2b804",
80
+ "Optimism": "0x2BB4FfD7E2c6D432b697554Efd77fA13bdbefd69",
81
+ "Robinhood": "0x2BB4FfD7E2c6D432b697554Efd77fA13bdbefd69"
75
82
  },
76
83
  "decimals": 18,
77
84
  "decimalsByChain": {
@@ -83,7 +90,8 @@
83
90
  "Base": "0xc30f1Ce05d1434d484E9A47283aA925fc8A8699a",
84
91
  "Mainnet": "0xc30f1Ce05d1434d484E9A47283aA925fc8A8699a",
85
92
  "Optimism": "0xc30f1Ce05d1434d484E9A47283aA925fc8A8699a",
86
- "Miden": "0xd17976f0809a8191412f2a126625df"
93
+ "Robinhood": "0xc30f1Ce05d1434d484E9A47283aA925fc8A8699a",
94
+ "Miden": "0xd41499b8f142ad9119fd9da357a3b2"
87
95
  },
88
96
  "decimals": 18,
89
97
  "decimalsByChain": {
@@ -94,8 +102,9 @@
94
102
  "contractAddress": {
95
103
  "Base": "0xc04d2869665Be874881133943523723Be5782720",
96
104
  "Mainnet": "0xc04d2869665Be874881133943523723Be5782720",
97
- "Miden": "0x6500ca8c2dd69e9147ab7eafad162c",
98
- "Optimism": "0xc04d2869665Be874881133943523723Be5782720"
105
+ "Miden": "0x0ead4362a745e7d1790d75ea779611",
106
+ "Optimism": "0xc04d2869665Be874881133943523723Be5782720",
107
+ "Robinhood": "0xc04d2869665Be874881133943523723Be5782720"
99
108
  },
100
109
  "decimals": 18,
101
110
  "decimalsByChain": {
@@ -107,7 +116,8 @@
107
116
  "Base": "0x7946dd86eE310D0aC16804A37787289Fa5b88A8A",
108
117
  "Mainnet": "0x7946dd86eE310D0aC16804A37787289Fa5b88A8A",
109
118
  "Optimism": "0x7946dd86eE310D0aC16804A37787289Fa5b88A8A",
110
- "Miden": "0x4a09f13153d9cd114c078bfb62a7ec"
119
+ "Robinhood": "0x7946dd86eE310D0aC16804A37787289Fa5b88A8A",
120
+ "Miden": "0x9368815c611d5d517914a2c1f0bc8d"
111
121
  },
112
122
  "decimals": 18,
113
123
  "decimalsByChain": {
@@ -119,7 +129,8 @@
119
129
  "Base": "0x9b2a2754a9182fD65360E23afCDf3BeFF51796E9",
120
130
  "Mainnet": "0x9b2a2754a9182fD65360E23afCDf3BeFF51796E9",
121
131
  "Optimism": "0x9b2a2754a9182fD65360E23afCDf3BeFF51796E9",
122
- "Miden": "0x5fd2e6fd17712c51404d09c2b847f7"
132
+ "Robinhood": "0x9b2a2754a9182fD65360E23afCDf3BeFF51796E9",
133
+ "Miden": "0x1b22d80328f58c5171f67a85f170a3"
123
134
  },
124
135
  "decimals": 18,
125
136
  "decimalsByChain": {
@@ -130,7 +141,8 @@
130
141
  "contractAddress": {
131
142
  "Base": "0xEA7dC9849206Ce73b11c465d37b85eC06B11Cf2C",
132
143
  "Mainnet": "0xEA7dC9849206Ce73b11c465d37b85eC06B11Cf2C",
133
- "Optimism": "0xEA7dC9849206Ce73b11c465d37b85eC06B11Cf2C"
144
+ "Optimism": "0xEA7dC9849206Ce73b11c465d37b85eC06B11Cf2C",
145
+ "Robinhood": "0xEA7dC9849206Ce73b11c465d37b85eC06B11Cf2C"
134
146
  },
135
147
  "decimals": 18
136
148
  },
@@ -138,7 +150,8 @@
138
150
  "contractAddress": {
139
151
  "Base": "0xB588418c0f90F07Bc9587d0050845a90C23C7502",
140
152
  "Mainnet": "0xB588418c0f90F07Bc9587d0050845a90C23C7502",
141
- "Optimism": "0xB588418c0f90F07Bc9587d0050845a90C23C7502"
153
+ "Optimism": "0xB588418c0f90F07Bc9587d0050845a90C23C7502",
154
+ "Robinhood": "0xB588418c0f90F07Bc9587d0050845a90C23C7502"
142
155
  },
143
156
  "decimals": 18
144
157
  },
@@ -146,7 +159,8 @@
146
159
  "contractAddress": {
147
160
  "Base": "0x512Ee6Bd7A4be5Ba4796F15Df080c4D0F89a38eD",
148
161
  "Mainnet": "0x512Ee6Bd7A4be5Ba4796F15Df080c4D0F89a38eD",
149
- "Optimism": "0x512Ee6Bd7A4be5Ba4796F15Df080c4D0F89a38eD"
162
+ "Optimism": "0x512Ee6Bd7A4be5Ba4796F15Df080c4D0F89a38eD",
163
+ "Robinhood": "0x512Ee6Bd7A4be5Ba4796F15Df080c4D0F89a38eD"
150
164
  },
151
165
  "decimals": 18
152
166
  },
@@ -154,7 +168,8 @@
154
168
  "contractAddress": {
155
169
  "Base": "0x145e03A80c19ad1b9d0429d06b6d52707de724A0",
156
170
  "Mainnet": "0x145e03A80c19ad1b9d0429d06b6d52707de724A0",
157
- "Optimism": "0x145e03A80c19ad1b9d0429d06b6d52707de724A0"
171
+ "Optimism": "0x145e03A80c19ad1b9d0429d06b6d52707de724A0",
172
+ "Robinhood": "0x145e03A80c19ad1b9d0429d06b6d52707de724A0"
158
173
  },
159
174
  "decimals": 18
160
175
  }
@@ -175,6 +190,10 @@
175
190
  "Optimism": {
176
191
  "chainId": 11155420,
177
192
  "explorer": "https://sepolia-optimistic.etherscan.io/"
193
+ },
194
+ "Robinhood": {
195
+ "chainId": 46630,
196
+ "explorer": "https://explorer.testnet.chain.robinhood.com"
178
197
  }
179
198
  },
180
199
  "relationships": {
@@ -338,15 +357,16 @@
338
357
  }
339
358
  },
340
359
  "tokenChains": {
341
- "DAI": ["Base", "Mainnet", "Optimism"],
342
- "USDT": ["Base", "Mainnet", "Optimism"],
343
- "USDC": ["Base", "Optimism", "Mainnet", "Miden"],
344
- "WETH": ["Base", "Mainnet", "Optimism"],
345
- "WBTC": ["Base", "Mainnet", "Optimism"],
346
- "PENGU": ["Base", "Mainnet", "Optimism"],
347
- "OSWALD": ["Base", "Mainnet", "Optimism"],
348
- "KICK": ["Base", "Mainnet", "Optimism"],
349
- "FERB": ["Base", "Mainnet", "Optimism"]
360
+ "USDCX": ["Miden"],
361
+ "DAI": ["Base", "Mainnet", "Optimism", "Robinhood"],
362
+ "USDT": ["Base", "Mainnet", "Optimism", "Robinhood"],
363
+ "USDC": ["Base", "Optimism", "Mainnet", "Miden", "Robinhood"],
364
+ "WETH": ["Base", "Mainnet", "Optimism", "Robinhood"],
365
+ "WBTC": ["Base", "Mainnet", "Optimism", "Robinhood"],
366
+ "PENGU": ["Base", "Mainnet", "Optimism", "Robinhood"],
367
+ "OSWALD": ["Base", "Mainnet", "Optimism", "Robinhood"],
368
+ "KICK": ["Base", "Mainnet", "Optimism", "Robinhood"],
369
+ "FERB": ["Base", "Mainnet", "Optimism", "Robinhood"]
350
370
  }
351
371
  }
352
372
  }
@@ -0,0 +1,24 @@
1
+ import type { Hex } from "viem";
2
+ export declare const DEX_POOLS_EXTRA_TYPESTRING = "string dexPools";
3
+ export declare const MAX_DEX_POOL_PREFERENCES = 16;
4
+ export type UniswapV3PoolPreference = {
5
+ protocol: "uniswap-v3";
6
+ chainId: number;
7
+ address: Hex;
8
+ };
9
+ export type UniswapV4PoolKey = {
10
+ currency0: Hex;
11
+ currency1: Hex;
12
+ fee: number;
13
+ tickSpacing: number;
14
+ hooks: Hex;
15
+ };
16
+ export type UniswapV4PoolPreference = {
17
+ protocol: "uniswap-v4";
18
+ chainId: number;
19
+ poolKey: UniswapV4PoolKey;
20
+ hookData?: Hex;
21
+ };
22
+ export type DexPoolPreference = UniswapV3PoolPreference | UniswapV4PoolPreference;
23
+ export declare function decodeDexPools(value: unknown): DexPoolPreference[];
24
+ export declare function encodeDexPools(pools: readonly DexPoolPreference[]): string;
@@ -0,0 +1,94 @@
1
+ export const DEX_POOLS_EXTRA_TYPESTRING = "string dexPools";
2
+ export const MAX_DEX_POOL_PREFERENCES = 16;
3
+ const DYNAMIC_FEE_FLAG = 0x800000;
4
+ function invalid(message) {
5
+ throw new Error(`Invalid dexPools: ${message}`);
6
+ }
7
+ function isHex(value) {
8
+ return typeof value === "string" && /^0x(?:[0-9a-fA-F]{2})*$/.test(value);
9
+ }
10
+ function address(value, field, allowZero = false) {
11
+ if (typeof value !== "string" ||
12
+ !/^0x[0-9a-fA-F]{40}$/.test(value) ||
13
+ (!allowZero && /^0x0{40}$/i.test(value))) {
14
+ return invalid(`${field} must be a non-zero address`);
15
+ }
16
+ return value.toLowerCase();
17
+ }
18
+ function integer(value, field, minimum, maximum) {
19
+ if (typeof value !== "number" ||
20
+ !Number.isSafeInteger(value) ||
21
+ value < minimum ||
22
+ value > maximum) {
23
+ return invalid(`${field} must be an integer between ${minimum} and ${maximum}`);
24
+ }
25
+ return value;
26
+ }
27
+ function decodeEntry(value) {
28
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
29
+ return invalid("each entry must be an object");
30
+ }
31
+ const entry = value;
32
+ const chainId = integer(entry.chainId, "chainId", 1, Number.MAX_SAFE_INTEGER);
33
+ if (entry.protocol === "uniswap-v3") {
34
+ return {
35
+ protocol: "uniswap-v3",
36
+ chainId,
37
+ address: address(entry.address, "address"),
38
+ };
39
+ }
40
+ if (entry.protocol !== "uniswap-v4") {
41
+ return invalid("protocol must be uniswap-v3 or uniswap-v4");
42
+ }
43
+ if (!entry.poolKey ||
44
+ typeof entry.poolKey !== "object" ||
45
+ Array.isArray(entry.poolKey)) {
46
+ return invalid("poolKey must be an object");
47
+ }
48
+ const key = entry.poolKey;
49
+ const currency0 = address(key.currency0, "poolKey.currency0", true);
50
+ const currency1 = address(key.currency1, "poolKey.currency1");
51
+ if (currency0 >= currency1) {
52
+ return invalid("poolKey.currency0 must sort before poolKey.currency1");
53
+ }
54
+ const fee = integer(key.fee, "poolKey.fee", 0, 0xffffff);
55
+ if (fee > 1_000_000 && fee !== DYNAMIC_FEE_FLAG) {
56
+ return invalid("poolKey.fee is outside the v4 fee range");
57
+ }
58
+ const tickSpacing = integer(key.tickSpacing, "poolKey.tickSpacing", 1, 32767);
59
+ const hooks = address(key.hooks, "poolKey.hooks", true);
60
+ const hookData = entry.hookData === undefined ? "0x" : entry.hookData;
61
+ if (!isHex(hookData))
62
+ return invalid("hookData must be even-length hex");
63
+ return {
64
+ protocol: "uniswap-v4",
65
+ chainId,
66
+ poolKey: { currency0, currency1, fee, tickSpacing, hooks },
67
+ hookData: hookData.toLowerCase(),
68
+ };
69
+ }
70
+ export function decodeDexPools(value) {
71
+ if (value === undefined || value === null || value === "")
72
+ return [];
73
+ if (typeof value !== "string")
74
+ return invalid("must be a JSON string");
75
+ let decoded;
76
+ try {
77
+ decoded = JSON.parse(value);
78
+ }
79
+ catch {
80
+ return invalid("must contain valid JSON");
81
+ }
82
+ if (!Array.isArray(decoded))
83
+ return invalid("must contain an array");
84
+ if (decoded.length > MAX_DEX_POOL_PREFERENCES) {
85
+ return invalid(`must contain at most ${MAX_DEX_POOL_PREFERENCES} entries`);
86
+ }
87
+ return decoded.map(decodeEntry);
88
+ }
89
+ export function encodeDexPools(pools) {
90
+ if (!Array.isArray(pools))
91
+ return invalid("must be an array");
92
+ // Reuse the decoder so encoding and decoding accept precisely the same schema.
93
+ return JSON.stringify(decodeDexPools(JSON.stringify(pools)));
94
+ }
@@ -0,0 +1,9 @@
1
+ /** Compact witness declaration for selecting an external quote provider. */
2
+ export declare const EXTERNAL_PROVIDER_EXTRA_TYPESTRING = "string provider";
3
+ export declare const EXTERNAL_QUOTE_PROVIDERS: readonly ["khalani", "near", "lifi"];
4
+ export type ExternalQuoteProvider = (typeof EXTERNAL_QUOTE_PROVIDERS)[number];
5
+ /**
6
+ * Validates an optional signed provider field without normalizing its value.
7
+ * The exact string is part of the Compact witness and must remain unchanged.
8
+ */
9
+ export declare function decodeExternalQuoteProvider(value: unknown): ExternalQuoteProvider | undefined;
@@ -0,0 +1,16 @@
1
+ /** Compact witness declaration for selecting an external quote provider. */
2
+ export const EXTERNAL_PROVIDER_EXTRA_TYPESTRING = "string provider";
3
+ export const EXTERNAL_QUOTE_PROVIDERS = ["khalani", "near", "lifi"];
4
+ /**
5
+ * Validates an optional signed provider field without normalizing its value.
6
+ * The exact string is part of the Compact witness and must remain unchanged.
7
+ */
8
+ export function decodeExternalQuoteProvider(value) {
9
+ if (value === undefined)
10
+ return undefined;
11
+ if (typeof value !== "string" ||
12
+ !EXTERNAL_QUOTE_PROVIDERS.includes(value)) {
13
+ throw new Error(`Invalid external provider; expected one of ${EXTERNAL_QUOTE_PROVIDERS.join(", ")}`);
14
+ }
15
+ return value;
16
+ }
@@ -0,0 +1,20 @@
1
+ import type { Graph } from "../types/index.js";
2
+ /** Resolve the canonical graph name and metadata from an EVM chain id. */
3
+ export declare function getGraphChain(graph: Graph, chainId: number): {
4
+ chainId: number;
5
+ explorer: string;
6
+ nativeCurrency?: {
7
+ name: string;
8
+ symbol: string;
9
+ decimals: number;
10
+ };
11
+ name: string;
12
+ } | undefined;
13
+ /** Resolve an asset only when its address exists on the requested chain. */
14
+ export declare function getGraphToken(graph: Graph, chainId: number, symbol: string): {
15
+ symbol: string;
16
+ address: string;
17
+ decimals: number;
18
+ } | undefined;
19
+ /** Check graph path eligibility, including wildcard filler protocols. */
20
+ export declare function supportsGraphProtocol(graph: Graph, chainId: number, protocol: string, symbol: string): boolean;
@@ -0,0 +1,34 @@
1
+ /** Resolve the canonical graph name and metadata from an EVM chain id. */
2
+ export function getGraphChain(graph, chainId) {
3
+ const entry = Object.entries(graph.chains).find(([, chain]) => chain.chainId === chainId);
4
+ return entry ? { name: entry[0], ...entry[1] } : undefined;
5
+ }
6
+ /** Resolve an asset only when its address exists on the requested chain. */
7
+ export function getGraphToken(graph, chainId, symbol) {
8
+ const chain = getGraphChain(graph, chainId);
9
+ const token = graph.tokens[symbol];
10
+ const address = chain && token?.contractAddress[chain.name];
11
+ return address && chain && token
12
+ ? {
13
+ symbol,
14
+ address,
15
+ decimals: token.decimalsByChain?.[chain.name] ?? token.decimals,
16
+ }
17
+ : undefined;
18
+ }
19
+ /** Check graph path eligibility, including wildcard filler protocols. */
20
+ export function supportsGraphProtocol(graph, chainId, protocol, symbol) {
21
+ const chain = getGraphChain(graph, chainId);
22
+ if (!chain || !getGraphToken(graph, chainId, symbol))
23
+ return false;
24
+ const addresses = graph.protocols[protocol]?.contractAddress;
25
+ if (!addresses?.[chain.name] && !addresses?.["*"])
26
+ return false;
27
+ const relationships = graph.relationships.protocolTokensByChain[protocol];
28
+ const relation = relationships?.[chain.name] ?? relationships?.["*"];
29
+ return [
30
+ relation?.tokensIn,
31
+ relation?.tokensOut,
32
+ relation?.tokensInAndOut,
33
+ ].some((tokens) => tokens?.includes(symbol) || tokens?.includes("*"));
34
+ }
@@ -1,4 +1,5 @@
1
1
  export * from "./compact.js";
2
+ export * from "./miden-id.js";
2
3
  export * from "./miden-intent.js";
3
4
  export * from "./path.js";
4
5
  export * from "./witness-typestring.js";
@@ -1,4 +1,5 @@
1
1
  export * from "./compact.js";
2
+ export * from "./miden-id.js";
2
3
  export * from "./miden-intent.js";
3
4
  export * from "./path.js";
4
5
  export * from "./witness-typestring.js";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Convert a Miden account or faucet identifier to canonical 0x-prefixed hex.
3
+ *
4
+ * Hex identifiers pass through with their body case preserved. Valid Miden
5
+ * Bech32/Bech32m addresses (including an optional interface suffix) are decoded.
6
+ * Invalid non-hex input is returned trimmed so callers can apply their own
7
+ * validation and surface the original value in error messages.
8
+ */
9
+ export declare function normalizeMidenId(id: string): string;
@@ -0,0 +1,88 @@
1
+ const BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
2
+ const BECH32_CHECKSUM = 1;
3
+ const BECH32M_CHECKSUM = 0x2bc830a3;
4
+ const MIDEN_ACCOUNT_ID_BYTES = 15;
5
+ function bech32Polymod(values) {
6
+ const generator = [
7
+ 0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3,
8
+ ];
9
+ let checksum = 1;
10
+ for (const value of values) {
11
+ const top = checksum >>> 25;
12
+ checksum = ((checksum & 0x1ffffff) << 5) ^ value;
13
+ for (let index = 0; index < generator.length; index += 1) {
14
+ if ((top >>> index) & 1)
15
+ checksum ^= generator[index];
16
+ }
17
+ }
18
+ return checksum >>> 0;
19
+ }
20
+ function bech32HrpExpand(hrp) {
21
+ const output = [];
22
+ for (let index = 0; index < hrp.length; index += 1) {
23
+ output.push(hrp.charCodeAt(index) >> 5);
24
+ }
25
+ output.push(0);
26
+ for (let index = 0; index < hrp.length; index += 1) {
27
+ output.push(hrp.charCodeAt(index) & 31);
28
+ }
29
+ return output;
30
+ }
31
+ function decodeMidenBech32(input) {
32
+ const address = input.split("_")[0].toLowerCase();
33
+ const separator = address.lastIndexOf("1");
34
+ if (separator < 1 || separator + 7 > address.length)
35
+ return null;
36
+ const words = [];
37
+ for (const character of address.slice(separator + 1)) {
38
+ const value = BECH32_CHARSET.indexOf(character);
39
+ if (value < 0)
40
+ return null;
41
+ words.push(value);
42
+ }
43
+ const checksum = bech32Polymod([
44
+ ...bech32HrpExpand(address.slice(0, separator)),
45
+ ...words,
46
+ ]);
47
+ if (checksum !== BECH32_CHECKSUM && checksum !== BECH32M_CHECKSUM) {
48
+ return null;
49
+ }
50
+ let accumulator = 0;
51
+ let bits = 0;
52
+ const bytes = [];
53
+ for (const word of words.slice(0, -6)) {
54
+ accumulator = (accumulator << 5) | word;
55
+ bits += 5;
56
+ while (bits >= 8) {
57
+ bits -= 8;
58
+ bytes.push((accumulator >> bits) & 0xff);
59
+ }
60
+ }
61
+ if (bytes.length < MIDEN_ACCOUNT_ID_BYTES + 1)
62
+ return null;
63
+ return ("0x" +
64
+ bytes
65
+ .slice(1, 1 + MIDEN_ACCOUNT_ID_BYTES)
66
+ .map((byte) => byte.toString(16).padStart(2, "0"))
67
+ .join(""));
68
+ }
69
+ /**
70
+ * Convert a Miden account or faucet identifier to canonical 0x-prefixed hex.
71
+ *
72
+ * Hex identifiers pass through with their body case preserved. Valid Miden
73
+ * Bech32/Bech32m addresses (including an optional interface suffix) are decoded.
74
+ * Invalid non-hex input is returned trimmed so callers can apply their own
75
+ * validation and surface the original value in error messages.
76
+ */
77
+ export function normalizeMidenId(id) {
78
+ const raw = (id ?? "").trim();
79
+ if (!raw)
80
+ return raw;
81
+ if (raw.startsWith("0x") || raw.startsWith("0X")) {
82
+ return "0x" + raw.slice(2);
83
+ }
84
+ if (/^[0-9a-fA-F]+$/.test(raw) && raw.length % 2 === 0) {
85
+ return "0x" + raw;
86
+ }
87
+ return decodeMidenBech32(raw) ?? raw;
88
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epoch-protocol/epoch-commons-sdk",
3
- "version": "0.1.20",
3
+ "version": "0.1.21-mainnet.0",
4
4
  "description": "SDK for interacting with Epoch Commons Protocol",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",