@shroud-fi/transport 0.1.3 → 0.1.4

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shroud-fi/transport",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Pluggable viem-based transport + canonical Base mainnet deployment manifest for the ShroudFi privacy SDK.",
5
5
  "keywords": [
6
6
  "shroudfi",
@@ -25,7 +25,10 @@
25
25
  }
26
26
  },
27
27
  "files": [
28
- "dist"
28
+ "dist",
29
+ "src",
30
+ "tsconfig.json",
31
+ "README.md"
29
32
  ],
30
33
  "peerDependencies": {
31
34
  "viem": "^2.21.0"
package/src/abis.ts ADDED
@@ -0,0 +1,288 @@
1
+ export const ERC5564AnnouncerAbi = [
2
+ {
3
+ type: 'function',
4
+ name: 'announce',
5
+ inputs: [
6
+ { name: 'schemeId', type: 'uint256' },
7
+ { name: 'stealthAddress', type: 'address' },
8
+ { name: 'ephemeralPubKey', type: 'bytes' },
9
+ { name: 'metadata', type: 'bytes' },
10
+ ],
11
+ outputs: [],
12
+ stateMutability: 'nonpayable',
13
+ },
14
+ {
15
+ type: 'event',
16
+ name: 'Announcement',
17
+ inputs: [
18
+ { name: 'schemeId', type: 'uint256', indexed: true },
19
+ { name: 'stealthAddress', type: 'address', indexed: true },
20
+ { name: 'caller', type: 'address', indexed: true },
21
+ { name: 'ephemeralPubKey', type: 'bytes', indexed: false },
22
+ { name: 'metadata', type: 'bytes', indexed: false },
23
+ ],
24
+ },
25
+ ] as const;
26
+
27
+ export const ShroudFiStealthAbi = [
28
+ {
29
+ type: 'function',
30
+ name: 'sendETH',
31
+ inputs: [
32
+ { name: 'stealthAddress', type: 'address' },
33
+ { name: 'ephemeralPubKey', type: 'bytes' },
34
+ { name: 'viewTag', type: 'bytes1' },
35
+ ],
36
+ outputs: [],
37
+ stateMutability: 'payable',
38
+ },
39
+ {
40
+ type: 'function',
41
+ name: 'sendERC20',
42
+ inputs: [
43
+ { name: 'stealthAddress', type: 'address' },
44
+ { name: 'token', type: 'address' },
45
+ { name: 'amount', type: 'uint256' },
46
+ { name: 'ephemeralPubKey', type: 'bytes' },
47
+ { name: 'viewTag', type: 'bytes1' },
48
+ ],
49
+ outputs: [],
50
+ stateMutability: 'nonpayable',
51
+ },
52
+ {
53
+ type: 'function',
54
+ name: 'ANNOUNCER',
55
+ inputs: [],
56
+ outputs: [{ name: '', type: 'address' }],
57
+ stateMutability: 'view',
58
+ },
59
+ {
60
+ type: 'function',
61
+ name: 'SCHEME_ID',
62
+ inputs: [],
63
+ outputs: [{ name: '', type: 'uint256' }],
64
+ stateMutability: 'view',
65
+ },
66
+ {
67
+ type: 'function',
68
+ name: 'ETH_TOKEN_ADDRESS',
69
+ inputs: [],
70
+ outputs: [{ name: '', type: 'address' }],
71
+ stateMutability: 'view',
72
+ },
73
+ {
74
+ type: 'event',
75
+ name: 'StealthPayment',
76
+ inputs: [
77
+ { name: 'stealthAddress', type: 'address', indexed: true },
78
+ { name: 'token', type: 'address', indexed: true },
79
+ { name: 'amount', type: 'uint256', indexed: false },
80
+ ],
81
+ },
82
+ { type: 'error', name: 'ZeroAddress', inputs: [] },
83
+ { type: 'error', name: 'ZeroAmount', inputs: [] },
84
+ { type: 'error', name: 'InsufficientETH', inputs: [] },
85
+ { type: 'error', name: 'ETHTransferFailed', inputs: [] },
86
+ ] as const;
87
+
88
+ /**
89
+ * Minimal ABI for USDC's EIP-3009 `transferWithAuthorization`. Pulled out as
90
+ * a separate constant because not every ERC-20 supports it — only Circle's
91
+ * USDC contract and a handful of other authorization-capable tokens. Use
92
+ * `USDC_BY_CHAIN[chainId]` to address the right deployment.
93
+ *
94
+ * The `authorizationState` view is included so callers can confirm a nonce
95
+ * has not already been consumed before submitting.
96
+ */
97
+ export const USDCAuthorizationAbi = [
98
+ {
99
+ type: 'function',
100
+ name: 'transferWithAuthorization',
101
+ inputs: [
102
+ { name: 'from', type: 'address' },
103
+ { name: 'to', type: 'address' },
104
+ { name: 'value', type: 'uint256' },
105
+ { name: 'validAfter', type: 'uint256' },
106
+ { name: 'validBefore', type: 'uint256' },
107
+ { name: 'nonce', type: 'bytes32' },
108
+ { name: 'v', type: 'uint8' },
109
+ { name: 'r', type: 'bytes32' },
110
+ { name: 's', type: 'bytes32' },
111
+ ],
112
+ outputs: [],
113
+ stateMutability: 'nonpayable',
114
+ },
115
+ {
116
+ type: 'function',
117
+ name: 'authorizationState',
118
+ inputs: [
119
+ { name: 'authorizer', type: 'address' },
120
+ { name: 'nonce', type: 'bytes32' },
121
+ ],
122
+ outputs: [{ name: '', type: 'bool' }],
123
+ stateMutability: 'view',
124
+ },
125
+ {
126
+ type: 'event',
127
+ name: 'AuthorizationUsed',
128
+ inputs: [
129
+ { name: 'authorizer', type: 'address', indexed: true },
130
+ { name: 'nonce', type: 'bytes32', indexed: true },
131
+ ],
132
+ },
133
+ ] as const;
134
+
135
+ export const ERC20Abi = [
136
+ {
137
+ type: 'function',
138
+ name: 'balanceOf',
139
+ inputs: [{ name: 'account', type: 'address' }],
140
+ outputs: [{ name: '', type: 'uint256' }],
141
+ stateMutability: 'view',
142
+ },
143
+ {
144
+ type: 'function',
145
+ name: 'transfer',
146
+ inputs: [
147
+ { name: 'to', type: 'address' },
148
+ { name: 'amount', type: 'uint256' },
149
+ ],
150
+ outputs: [{ name: '', type: 'bool' }],
151
+ stateMutability: 'nonpayable',
152
+ },
153
+ {
154
+ type: 'function',
155
+ name: 'approve',
156
+ inputs: [
157
+ { name: 'spender', type: 'address' },
158
+ { name: 'amount', type: 'uint256' },
159
+ ],
160
+ outputs: [{ name: '', type: 'bool' }],
161
+ stateMutability: 'nonpayable',
162
+ },
163
+ {
164
+ type: 'function',
165
+ name: 'allowance',
166
+ inputs: [
167
+ { name: 'owner', type: 'address' },
168
+ { name: 'spender', type: 'address' },
169
+ ],
170
+ outputs: [{ name: '', type: 'uint256' }],
171
+ stateMutability: 'view',
172
+ },
173
+ ] as const;
174
+
175
+ export const ShroudFiRelayerAbi = [
176
+ {
177
+ type: 'function',
178
+ name: 'sweepERC20WithPermit',
179
+ inputs: [
180
+ { name: 'token', type: 'address' },
181
+ { name: 'destination', type: 'address' },
182
+ { name: 'deadline', type: 'uint256' },
183
+ { name: 'v', type: 'uint8' },
184
+ { name: 'r', type: 'bytes32' },
185
+ { name: 's', type: 'bytes32' },
186
+ ],
187
+ outputs: [],
188
+ stateMutability: 'nonpayable',
189
+ },
190
+ {
191
+ type: 'function',
192
+ name: 'previewFee',
193
+ inputs: [{ name: 'grossAmount', type: 'uint256' }],
194
+ outputs: [
195
+ { name: 'fee', type: 'uint256' },
196
+ { name: 'net', type: 'uint256' },
197
+ ],
198
+ stateMutability: 'view',
199
+ },
200
+ {
201
+ type: 'function',
202
+ name: 'FEE_BPS',
203
+ inputs: [],
204
+ outputs: [{ name: '', type: 'uint16' }],
205
+ stateMutability: 'view',
206
+ },
207
+ {
208
+ type: 'function',
209
+ name: 'BPS_DENOMINATOR',
210
+ inputs: [],
211
+ outputs: [{ name: '', type: 'uint16' }],
212
+ stateMutability: 'view',
213
+ },
214
+ {
215
+ type: 'function',
216
+ name: 'TREASURY',
217
+ inputs: [],
218
+ outputs: [{ name: '', type: 'address' }],
219
+ stateMutability: 'view',
220
+ },
221
+ {
222
+ type: 'function',
223
+ name: 'treasury',
224
+ inputs: [],
225
+ outputs: [{ name: '', type: 'address' }],
226
+ stateMutability: 'view',
227
+ },
228
+ {
229
+ type: 'function',
230
+ name: 'trustedForwarder',
231
+ inputs: [],
232
+ outputs: [{ name: '', type: 'address' }],
233
+ stateMutability: 'view',
234
+ },
235
+ {
236
+ type: 'function',
237
+ name: 'isTrustedForwarder',
238
+ inputs: [{ name: 'forwarder', type: 'address' }],
239
+ outputs: [{ name: '', type: 'bool' }],
240
+ stateMutability: 'view',
241
+ },
242
+ {
243
+ type: 'event',
244
+ name: 'SweepRelayed',
245
+ inputs: [
246
+ { name: 'stealthAddress', type: 'address', indexed: true },
247
+ { name: 'token', type: 'address', indexed: true },
248
+ { name: 'destination', type: 'address', indexed: false },
249
+ { name: 'grossAmount', type: 'uint256', indexed: false },
250
+ { name: 'feeAmount', type: 'uint256', indexed: false },
251
+ ],
252
+ },
253
+ { type: 'error', name: 'InvalidToken', inputs: [] },
254
+ { type: 'error', name: 'InvalidDestination', inputs: [] },
255
+ { type: 'error', name: 'ZeroBalance', inputs: [] },
256
+ { type: 'error', name: 'PermitFailedAndNoAllowance', inputs: [] },
257
+ { type: 'error', name: 'TransferFailed', inputs: [] },
258
+ ] as const;
259
+
260
+ export const ERC6538RegistryAbi = [
261
+ {
262
+ type: 'function',
263
+ name: 'registerKeys',
264
+ inputs: [
265
+ { name: 'schemeId', type: 'uint256' },
266
+ { name: 'stealthMetaAddress', type: 'bytes' },
267
+ ],
268
+ outputs: [],
269
+ stateMutability: 'nonpayable',
270
+ },
271
+ {
272
+ type: 'function',
273
+ name: 'stealthMetaAddressOf',
274
+ inputs: [
275
+ { name: 'registrant', type: 'address' },
276
+ { name: 'schemeId', type: 'uint256' },
277
+ ],
278
+ outputs: [{ name: '', type: 'bytes' }],
279
+ stateMutability: 'view',
280
+ },
281
+ {
282
+ type: 'function',
283
+ name: 'incrementNonce',
284
+ inputs: [{ name: 'schemeId', type: 'uint256' }],
285
+ outputs: [],
286
+ stateMutability: 'nonpayable',
287
+ },
288
+ ] as const;
@@ -0,0 +1,102 @@
1
+ import type { Address } from 'viem';
2
+
3
+ /** Canonical ERC-5564 stealth announcement contract — same address every EVM chain (CREATE2). */
4
+ export const ERC5564_ANNOUNCER = '0x55649E01B5Df198D18D95b5cc5051630cfD45564' as const;
5
+
6
+ /** Canonical ERC-6538 stealth meta-address registry — same address every EVM chain (CREATE2). */
7
+ export const ERC6538_REGISTRY = '0x6538E6bf4B0eBd30A8Ea093027Ac2422ce5d6538' as const;
8
+
9
+ /**
10
+ * Canonical USDC (Circle-issued) contract addresses, keyed by chain id.
11
+ *
12
+ * Per-chain because USDC is NOT CREATE2-deterministic across chains — Circle
13
+ * deploys a native contract per network with a different address each time.
14
+ *
15
+ * Coverage (P6 v1):
16
+ * - 8453 Base mainnet — Circle-native USDC
17
+ * - 84532 Base Sepolia — Circle-native testnet USDC
18
+ *
19
+ * Both addresses verified against Circle's official deployment registry on
20
+ * 2026-06-03. Adding new chains: confirm via https://developers.circle.com/
21
+ * stablecoin/docs/usdc-on-other-chains before extending this map.
22
+ *
23
+ * Both contracts implement EIP-3009 `transferWithAuthorization` (the signing
24
+ * primitive x402's `exact` scheme depends on) AND EIP-2612 `permit` (the
25
+ * signing primitive `ShroudFiRelayer` uses). Either path drives sweeps for
26
+ * USDC stealth payments — see `@shroud-fi/payments` for the dispatch logic.
27
+ */
28
+ export const USDC_BY_CHAIN: Readonly<Record<number, Address>> = Object.freeze({
29
+ 8453: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
30
+ 84532: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
31
+ } as const);
32
+
33
+ /**
34
+ * EIP-712 domain metadata for USDC per chain. Required for client agents
35
+ * signing `TransferWithAuthorization` against the correct domain separator.
36
+ *
37
+ * USDC on Base mainnet + Sepolia both use the same domain shape (Circle's
38
+ * standard issuance contract). Other chains MAY differ — re-verify before
39
+ * extending.
40
+ */
41
+ export const USDC_EIP712_DOMAIN_BY_CHAIN: Readonly<
42
+ Record<number, { readonly name: string; readonly version: string }>
43
+ > = Object.freeze({
44
+ 8453: Object.freeze({ name: 'USD Coin', version: '2' }),
45
+ 84532: Object.freeze({ name: 'USDC', version: '2' }),
46
+ } as const);
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // EIP-3009 token registry (v0.1.1 — multi-currency x402)
50
+ // ---------------------------------------------------------------------------
51
+
52
+ /**
53
+ * EIP-3009 token entry. Pins everything `@shroud-fi/x402` and any future
54
+ * EIP-3009-using package needs to construct a valid `transferWithAuthorization`
55
+ * signature.
56
+ *
57
+ * Membership rule: a token belongs here ONLY if its on-chain contract
58
+ * implements EIP-3009 `transferWithAuthorization`. USDT (vanilla ERC-20),
59
+ * DAI (DAI-style permit), and most ERC-20s do NOT qualify.
60
+ */
61
+ export interface Eip3009Token {
62
+ readonly symbol: 'USDC' | 'EURC';
63
+ readonly address: Address;
64
+ readonly decimals: number;
65
+ readonly domain: { readonly name: string; readonly version: string };
66
+ }
67
+
68
+ /**
69
+ * Per-chain EIP-3009 token list. Both x402 settlement (via
70
+ * `transferWithAuthorization`) and any future EIP-3009-based sweep paths
71
+ * read from here.
72
+ *
73
+ * Adding a new chain or token: VERIFY the contract on-chain (via
74
+ * `contract.name()` + `contract.version()`) before extending — the EIP-712
75
+ * domain MUST match exactly or signature recovery silently fails.
76
+ */
77
+ export const EIP3009_TOKENS_BY_CHAIN: Readonly<
78
+ Record<number, ReadonlyArray<Eip3009Token>>
79
+ > = Object.freeze({
80
+ 8453: Object.freeze([
81
+ Object.freeze({
82
+ symbol: 'USDC' as const,
83
+ address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as Address,
84
+ decimals: 6,
85
+ domain: Object.freeze({ name: 'USD Coin', version: '2' }),
86
+ }),
87
+ Object.freeze({
88
+ symbol: 'EURC' as const,
89
+ address: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42' as Address,
90
+ decimals: 6,
91
+ domain: Object.freeze({ name: 'EURC', version: '1' }),
92
+ }),
93
+ ]),
94
+ 84532: Object.freeze([
95
+ Object.freeze({
96
+ symbol: 'USDC' as const,
97
+ address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e' as Address,
98
+ decimals: 6,
99
+ domain: Object.freeze({ name: 'USDC', version: '2' }),
100
+ }),
101
+ ]),
102
+ } as const);
package/src/chains.ts ADDED
@@ -0,0 +1 @@
1
+ export { base, baseSepolia } from 'viem/chains';
package/src/create.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { createPublicClient, createWalletClient, http, type Chain } from 'viem';
2
+ import { base, baseSepolia } from 'viem/chains';
3
+ import { privateKeyToAccount } from 'viem/accounts';
4
+ import type { ShroudFiTransport, ShroudFiTransportConfig } from './types.js';
5
+
6
+ export function createTransport(config: ShroudFiTransportConfig): ShroudFiTransport {
7
+ const chain: Chain = config.chain === 'base' ? base : baseSepolia;
8
+ const transport = config.rpcUrl ? http(config.rpcUrl) : http();
9
+
10
+ const publicClient = createPublicClient({ chain, transport });
11
+
12
+ let walletClient: ShroudFiTransport['walletClient'];
13
+ if (config.privateKey) {
14
+ const account = privateKeyToAccount(config.privateKey);
15
+ walletClient = createWalletClient({ account, chain, transport });
16
+ }
17
+
18
+ return { publicClient, walletClient, chain } as ShroudFiTransport;
19
+ }
@@ -0,0 +1,153 @@
1
+ import type { Address } from 'viem';
2
+ import {
3
+ DEPLOYMENTS,
4
+ SHROUDFI_STEALTH_BY_CHAIN,
5
+ SHROUDFI_REGISTRAR_BY_CHAIN,
6
+ MOCK_ERC20_BY_CHAIN,
7
+ MOCK_ERC20_PERMIT_BY_CHAIN,
8
+ SHROUDFI_RELAYER_BY_CHAIN,
9
+ SHROUDFI_ETH_RELAYER_BY_CHAIN,
10
+ } from './deployments.js';
11
+ import type { DeploymentManifest } from './deployments.js';
12
+ import {
13
+ USDC_BY_CHAIN,
14
+ USDC_EIP712_DOMAIN_BY_CHAIN,
15
+ EIP3009_TOKENS_BY_CHAIN,
16
+ } from './addresses.js';
17
+ import type { Eip3009Token } from './addresses.js';
18
+ import { UnknownChainError } from './errors.js';
19
+
20
+ export function getShroudFiStealth(chainId: number): Address {
21
+ const a = SHROUDFI_STEALTH_BY_CHAIN[chainId];
22
+ if (!a) throw new UnknownChainError(chainId);
23
+ return a;
24
+ }
25
+
26
+ export function getShroudFiRegistrar(chainId: number): Address {
27
+ const a = SHROUDFI_REGISTRAR_BY_CHAIN[chainId];
28
+ if (!a) throw new UnknownChainError(chainId);
29
+ return a;
30
+ }
31
+
32
+ /**
33
+ * MockERC20 lookup. Returns undefined for chains that don't carry one
34
+ * (e.g. mainnet — mocks are testnet-only).
35
+ */
36
+ export function getMockERC20(chainId: number): Address | undefined {
37
+ return MOCK_ERC20_BY_CHAIN[chainId];
38
+ }
39
+
40
+ /**
41
+ * ShroudFiRelayer lookup. Returns undefined for chains where the relayer
42
+ * contract has not yet been deployed (e.g. before Wave 3 populates the manifest).
43
+ */
44
+ export function getRelayer(chainId: number): Address | undefined {
45
+ return SHROUDFI_RELAYER_BY_CHAIN[chainId];
46
+ }
47
+
48
+ /**
49
+ * ShroudFiEthRelayer (P5.1) lookup. Returns undefined for chains where the
50
+ * EIP-7702 ETH gasless sweep contract has not yet been deployed.
51
+ */
52
+ export function getEthRelayer(chainId: number): Address | undefined {
53
+ return SHROUDFI_ETH_RELAYER_BY_CHAIN[chainId];
54
+ }
55
+
56
+ /**
57
+ * MockERC20Permit lookup — the permit-capable ERC-20 mock used by the demo
58
+ * for both direct sweep AND gasless sweep paths. The original MockERC20
59
+ * (non-permit) remains in the manifest as a backward-compatibility entry
60
+ * but new code paths should prefer this one.
61
+ *
62
+ * Returns undefined for chains that don't carry a deployment (mainnet etc.).
63
+ */
64
+ export function getMockERC20Permit(chainId: number): Address | undefined {
65
+ return MOCK_ERC20_PERMIT_BY_CHAIN[chainId];
66
+ }
67
+
68
+ /**
69
+ * Canonical USDC (Circle-issued) address lookup. Returns undefined for any
70
+ * chain not in the supported allowlist — callers should treat undefined as
71
+ * "USDC not available on this chain" and surface a clear error, NOT fall back
72
+ * to a zero address or a non-Circle token.
73
+ */
74
+ export function getUSDC(chainId: number): Address | undefined {
75
+ return USDC_BY_CHAIN[chainId];
76
+ }
77
+
78
+ /**
79
+ * EIP-712 domain metadata (`name`, `version`) for USDC on a given chain.
80
+ * Required to sign `TransferWithAuthorization` for x402's `exact` scheme and
81
+ * for `ShroudFiRelayer` ERC-20 permit-based gasless sweeps.
82
+ *
83
+ * Returns undefined when USDC is not supported on the chain.
84
+ */
85
+ export function getUSDCDomain(
86
+ chainId: number,
87
+ ): { readonly name: string; readonly version: string } | undefined {
88
+ return USDC_EIP712_DOMAIN_BY_CHAIN[chainId];
89
+ }
90
+
91
+ /**
92
+ * Boolean check — is USDC available (and supported by this SDK) on this chain?
93
+ */
94
+ export function hasUSDC(chainId: number): boolean {
95
+ return chainId in USDC_BY_CHAIN;
96
+ }
97
+
98
+ // ---------------------------------------------------------------------------
99
+ // EIP-3009 token registry helpers (v0.1.1 — multi-currency x402)
100
+ // ---------------------------------------------------------------------------
101
+
102
+ /**
103
+ * Look up an EIP-3009 token by chain id + on-chain address. Address comparison
104
+ * is case-insensitive — callers can pass either checksummed or lowercase form.
105
+ *
106
+ * Returns undefined when the chain has no EIP-3009 tokens registered OR the
107
+ * address is not one of them.
108
+ */
109
+ export function getEip3009Token(
110
+ chainId: number,
111
+ address: string,
112
+ ): Eip3009Token | undefined {
113
+ const list = EIP3009_TOKENS_BY_CHAIN[chainId];
114
+ if (list === undefined) return undefined;
115
+ const needle = address.toLowerCase();
116
+ return list.find((t) => t.address.toLowerCase() === needle);
117
+ }
118
+
119
+ /**
120
+ * Look up an EIP-3009 token by symbol ('USDC', 'EURC'). Returns undefined
121
+ * for unknown chains or unsupported symbols (USDT, DAI, etc).
122
+ */
123
+ export function getEip3009TokenBySymbol(
124
+ chainId: number,
125
+ symbol: string,
126
+ ): Eip3009Token | undefined {
127
+ const list = EIP3009_TOKENS_BY_CHAIN[chainId];
128
+ if (list === undefined) return undefined;
129
+ return list.find((t) => t.symbol === symbol);
130
+ }
131
+
132
+ /**
133
+ * Enumerate every EIP-3009 token registered for a given chain. Useful for
134
+ * surfacing the available-assets list in an x402 challenge or a UI picker.
135
+ * Returns the empty array for unknown chains.
136
+ */
137
+ export function listEip3009Tokens(chainId: number): ReadonlyArray<Eip3009Token> {
138
+ return EIP3009_TOKENS_BY_CHAIN[chainId] ?? [];
139
+ }
140
+
141
+ export function getDeployment(chainId: number): DeploymentManifest {
142
+ const m = DEPLOYMENTS[chainId];
143
+ if (!m) throw new UnknownChainError(chainId);
144
+ return m;
145
+ }
146
+
147
+ export function hasDeployment(chainId: number): boolean {
148
+ return Boolean(DEPLOYMENTS[chainId]);
149
+ }
150
+
151
+ export function listDeployedChains(): readonly number[] {
152
+ return Object.keys(DEPLOYMENTS).map(Number);
153
+ }
@@ -0,0 +1,110 @@
1
+ // AUTO-GENERATED FILE — DO NOT EDIT.
2
+ // Source of truth: contracts/deployments/*.json
3
+ // Regenerate via: pnpm sync-deployments
4
+ // (To produce/update a manifest entry, deploy via Foundry and capture the MANIFEST line.)
5
+
6
+ import type { Address, Hex } from 'viem';
7
+
8
+ export interface DeploymentManifest {
9
+ readonly chainId: number;
10
+ readonly chainName: string;
11
+ readonly deployedAt: string;
12
+ readonly deployer: Address;
13
+ readonly shroudfiStealth: Address;
14
+ readonly shroudfiRegistrar: Address;
15
+ readonly stealthSalt: Hex;
16
+ readonly registrarSalt: Hex;
17
+ readonly announcer: Address;
18
+ readonly registry: Address;
19
+ readonly block: number;
20
+ readonly blockscoutUrl?: string;
21
+ readonly mockERC20?: Address;
22
+ readonly mockERC20Block?: number;
23
+ readonly shroudfiRelayer?: Address;
24
+ readonly relayerTreasury?: Address;
25
+ readonly relayerForwarder?: Address;
26
+ readonly relayerBlock?: number;
27
+ readonly mockERC20Permit?: Address;
28
+ readonly mockERC20PermitBlock?: number;
29
+ readonly shroudfiEthRelayer?: Address;
30
+ readonly shroudfiEthRelayerBlock?: number;
31
+ readonly ethRelayerOperator?: Address;
32
+ readonly isExample?: boolean;
33
+ }
34
+
35
+ export const DEPLOYMENTS: Readonly<Record<number, DeploymentManifest>> = Object.freeze({
36
+ 8453: Object.freeze({
37
+ chainId: 8453,
38
+ chainName: "Base",
39
+ deployedAt: "2026-06-02T23:30:00Z",
40
+ deployer: "0xBCB3E28150982F2EfeC615b4f8D15Cf1A1C418Aa",
41
+ shroudfiStealth: "0x4BC88813b09dDD6Ab530261200D706B397EcD0AB",
42
+ shroudfiRegistrar: "0xDbBB9Ebc61b800a3d3309A867a87439F706C1207",
43
+ stealthSalt: "0x36f380f35c71197d157fceabdd952cfe52e65d056c5d5cf73d72f407147c0961",
44
+ registrarSalt: "0xad705f075979fd59f9335b1123d73d1f3c73955b9fad97c337b25b6054fdf95d",
45
+ announcer: "0x55649E01B5Df198D18D95b5cc5051630cfD45564",
46
+ registry: "0x6538E6bf4B0eBd30A8Ea093027Ac2422ce5d6538",
47
+ block: 46825277,
48
+ blockscoutUrl: "https://base.blockscout.com",
49
+ shroudfiRelayer: "0x44f35ED32516AE8247aF5ec4ED5d5BEb501631d1",
50
+ relayerTreasury: "0x4B01C6eadD1663f5A7d792C24d8001c80EAE25b2",
51
+ relayerForwarder: "0xBCB3E28150982F2EfeC615b4f8D15Cf1A1C418Aa",
52
+ relayerBlock: 46825293,
53
+ shroudfiEthRelayer: "0x477c497F17cED01ff9c01D8a9Da1c019AFb31a66",
54
+ shroudfiEthRelayerBlock: 46825310,
55
+ ethRelayerOperator: "0xBCB3E28150982F2EfeC615b4f8D15Cf1A1C418Aa",
56
+ } as const satisfies DeploymentManifest),
57
+ 84532: Object.freeze({
58
+ chainId: 84532,
59
+ chainName: "Base Sepolia",
60
+ deployedAt: "2026-05-31T20:48:00Z",
61
+ deployer: "0xdEBC95C65ACf33B6192981D132dd8B738Edb7ACc",
62
+ shroudfiStealth: "0x4BC88813b09dDD6Ab530261200D706B397EcD0AB",
63
+ shroudfiRegistrar: "0xDbBB9Ebc61b800a3d3309A867a87439F706C1207",
64
+ stealthSalt: "0x36f380f35c71197d157fceabdd952cfe52e65d056c5d5cf73d72f407147c0961",
65
+ registrarSalt: "0xad705f075979fd59f9335b1123d73d1f3c73955b9fad97c337b25b6054fdf95d",
66
+ announcer: "0x55649E01B5Df198D18D95b5cc5051630cfD45564",
67
+ registry: "0x6538E6bf4B0eBd30A8Ea093027Ac2422ce5d6538",
68
+ block: 42245336,
69
+ blockscoutUrl: "https://base-sepolia.blockscout.com",
70
+ mockERC20: "0xd2ab70198E62C958eCF81cc4AbD6c9108caa9649",
71
+ mockERC20Block: 42249078,
72
+ shroudfiRelayer: "0x598E511ad360D38d5895E426451DB5B384852b39",
73
+ relayerTreasury: "0x4B01C6eadD1663f5A7d792C24d8001c80EAE25b2",
74
+ relayerForwarder: "0xdEBC95C65ACf33B6192981D132dd8B738Edb7ACc",
75
+ relayerBlock: 42280627,
76
+ mockERC20Permit: "0xEA1F4516cB2D94E7A16bbc36fBECFB030d595dD5",
77
+ mockERC20PermitBlock: 42277427,
78
+ shroudfiEthRelayer: "0xC38530004A65993435E204dE6fEB666F6917a34E",
79
+ shroudfiEthRelayerBlock: 42328886,
80
+ ethRelayerOperator: "0xdEBC95C65ACf33B6192981D132dd8B738Edb7ACc",
81
+ } as const satisfies DeploymentManifest),
82
+ } as const);
83
+
84
+ export const SHROUDFI_STEALTH_BY_CHAIN: Readonly<Record<number, Address>> = Object.freeze({
85
+ 8453: "0x4BC88813b09dDD6Ab530261200D706B397EcD0AB",
86
+ 84532: "0x4BC88813b09dDD6Ab530261200D706B397EcD0AB",
87
+ } as const);
88
+
89
+ export const SHROUDFI_REGISTRAR_BY_CHAIN: Readonly<Record<number, Address>> = Object.freeze({
90
+ 8453: "0xDbBB9Ebc61b800a3d3309A867a87439F706C1207",
91
+ 84532: "0xDbBB9Ebc61b800a3d3309A867a87439F706C1207",
92
+ } as const);
93
+
94
+ export const MOCK_ERC20_BY_CHAIN: Readonly<Record<number, Address>> = Object.freeze({
95
+ 84532: "0xd2ab70198E62C958eCF81cc4AbD6c9108caa9649",
96
+ } as const);
97
+
98
+ export const SHROUDFI_RELAYER_BY_CHAIN: Readonly<Record<number, Address>> = Object.freeze({
99
+ 8453: "0x44f35ED32516AE8247aF5ec4ED5d5BEb501631d1",
100
+ 84532: "0x598E511ad360D38d5895E426451DB5B384852b39",
101
+ } as const);
102
+
103
+ export const MOCK_ERC20_PERMIT_BY_CHAIN: Readonly<Record<number, Address>> = Object.freeze({
104
+ 84532: "0xEA1F4516cB2D94E7A16bbc36fBECFB030d595dD5",
105
+ } as const);
106
+
107
+ export const SHROUDFI_ETH_RELAYER_BY_CHAIN: Readonly<Record<number, Address>> = Object.freeze({
108
+ 8453: "0x477c497F17cED01ff9c01D8a9Da1c019AFb31a66",
109
+ 84532: "0xC38530004A65993435E204dE6fEB666F6917a34E",
110
+ } as const);
package/src/errors.ts ADDED
@@ -0,0 +1,13 @@
1
+ export class UnknownChainError extends Error {
2
+ override readonly name: string = 'UnknownChainError';
3
+ constructor(chainId: number) {
4
+ super(`No ShroudFi deployment found for chainId ${chainId}`);
5
+ }
6
+ }
7
+
8
+ export class InvalidManifestError extends Error {
9
+ override readonly name: string = 'InvalidManifestError';
10
+ constructor(reason: string) {
11
+ super(`Invalid deployment manifest: ${reason}`);
12
+ }
13
+ }
package/src/index.ts ADDED
@@ -0,0 +1,47 @@
1
+ export { createTransport } from './create.js';
2
+ export type { ShroudFiTransport, ShroudFiTransportConfig, SendOptions } from './types.js';
3
+ export { base, baseSepolia } from './chains.js';
4
+ export {
5
+ ERC5564_ANNOUNCER,
6
+ ERC6538_REGISTRY,
7
+ USDC_BY_CHAIN,
8
+ USDC_EIP712_DOMAIN_BY_CHAIN,
9
+ EIP3009_TOKENS_BY_CHAIN,
10
+ } from './addresses.js';
11
+ export type { Eip3009Token } from './addresses.js';
12
+ export {
13
+ ERC5564AnnouncerAbi,
14
+ ERC6538RegistryAbi,
15
+ ShroudFiStealthAbi,
16
+ ShroudFiRelayerAbi,
17
+ ERC20Abi,
18
+ USDCAuthorizationAbi,
19
+ } from './abis.js';
20
+ export {
21
+ getShroudFiStealth,
22
+ getShroudFiRegistrar,
23
+ getMockERC20,
24
+ getMockERC20Permit,
25
+ getRelayer,
26
+ getEthRelayer,
27
+ getUSDC,
28
+ getUSDCDomain,
29
+ hasUSDC,
30
+ getEip3009Token,
31
+ getEip3009TokenBySymbol,
32
+ listEip3009Tokens,
33
+ getDeployment,
34
+ hasDeployment,
35
+ listDeployedChains,
36
+ } from './deployments-helpers.js';
37
+ export type { DeploymentManifest } from './deployments.js';
38
+ export {
39
+ DEPLOYMENTS,
40
+ SHROUDFI_STEALTH_BY_CHAIN,
41
+ SHROUDFI_REGISTRAR_BY_CHAIN,
42
+ MOCK_ERC20_BY_CHAIN,
43
+ MOCK_ERC20_PERMIT_BY_CHAIN,
44
+ SHROUDFI_RELAYER_BY_CHAIN,
45
+ SHROUDFI_ETH_RELAYER_BY_CHAIN,
46
+ } from './deployments.js';
47
+ export { UnknownChainError, InvalidManifestError } from './errors.js';
package/src/types.ts ADDED
@@ -0,0 +1,19 @@
1
+ import type { PublicClient, WalletClient, Chain, Transport } from 'viem';
2
+
3
+ export interface ShroudFiTransportConfig {
4
+ readonly chain: 'base' | 'baseSepolia';
5
+ readonly rpcUrl?: string;
6
+ readonly privateKey?: `0x${string}`;
7
+ }
8
+
9
+ export interface ShroudFiTransport {
10
+ readonly publicClient: PublicClient<Transport, Chain>;
11
+ readonly walletClient?: WalletClient<Transport, Chain>;
12
+ readonly chain: Chain;
13
+ }
14
+
15
+ export interface SendOptions {
16
+ readonly maxFeePerGas?: bigint;
17
+ readonly maxPriorityFeePerGas?: bigint;
18
+ readonly nonce?: number;
19
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist/esm",
5
+ "rootDir": "./src"
6
+ },
7
+ "include": ["src/**/*.ts"],
8
+ "exclude": ["dist", "test", "node_modules"]
9
+ }