@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 +5 -2
- package/src/abis.ts +288 -0
- package/src/addresses.ts +102 -0
- package/src/chains.ts +1 -0
- package/src/create.ts +19 -0
- package/src/deployments-helpers.ts +153 -0
- package/src/deployments.ts +110 -0
- package/src/errors.ts +13 -0
- package/src/index.ts +47 -0
- package/src/types.ts +19 -0
- package/tsconfig.json +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shroud-fi/transport",
|
|
3
|
-
"version": "0.1.
|
|
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;
|
package/src/addresses.ts
ADDED
|
@@ -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
|
+
}
|