@shroud-fi/agent-runtime 0.1.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.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/cjs/agent.d.ts +235 -0
- package/dist/cjs/agent.d.ts.map +1 -0
- package/dist/cjs/agent.js +594 -0
- package/dist/cjs/agent.js.map +1 -0
- package/dist/cjs/browser.d.ts +38 -0
- package/dist/cjs/browser.d.ts.map +1 -0
- package/dist/cjs/browser.js +94 -0
- package/dist/cjs/browser.js.map +1 -0
- package/dist/cjs/constants.d.ts +17 -0
- package/dist/cjs/constants.d.ts.map +1 -0
- package/dist/cjs/constants.js +20 -0
- package/dist/cjs/constants.js.map +1 -0
- package/dist/cjs/errors.d.ts +107 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +152 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/factory.d.ts +19 -0
- package/dist/cjs/factory.d.ts.map +1 -0
- package/dist/cjs/factory.js +46 -0
- package/dist/cjs/factory.js.map +1 -0
- package/dist/cjs/index.d.ts +6 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +37 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/types.d.ts +151 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +10 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/agent.d.ts +235 -0
- package/dist/esm/agent.d.ts.map +1 -0
- package/dist/esm/agent.js +557 -0
- package/dist/esm/agent.js.map +1 -0
- package/dist/esm/browser.d.ts +38 -0
- package/dist/esm/browser.d.ts.map +1 -0
- package/dist/esm/browser.js +87 -0
- package/dist/esm/browser.js.map +1 -0
- package/dist/esm/constants.d.ts +17 -0
- package/dist/esm/constants.d.ts.map +1 -0
- package/dist/esm/constants.js +17 -0
- package/dist/esm/constants.js.map +1 -0
- package/dist/esm/errors.d.ts +107 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +137 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/factory.d.ts +19 -0
- package/dist/esm/factory.d.ts.map +1 -0
- package/dist/esm/factory.js +43 -0
- package/dist/esm/factory.js.map +1 -0
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/types.d.ts +151 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +9 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ShroudAgent — the ergonomic runtime that composes core + payments + scanning
|
|
4
|
+
* + transport into a single class.
|
|
5
|
+
*
|
|
6
|
+
* Privacy invariants (binding — code-reviewer will check):
|
|
7
|
+
* - No console.* anywhere in this file.
|
|
8
|
+
* - No JSON.stringify of keys, identity, detection, signature, or masterSeed.
|
|
9
|
+
* - No key bytes / signature bytes in error messages.
|
|
10
|
+
* - No amount values in error messages.
|
|
11
|
+
* - The spend key never leaves runtime; the scanning module needs it for
|
|
12
|
+
* ECDH derivation only.
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.ShroudAgent = void 0;
|
|
49
|
+
const viem_1 = require("viem");
|
|
50
|
+
const core_1 = require("@shroud-fi/core");
|
|
51
|
+
const payments_1 = require("@shroud-fi/payments");
|
|
52
|
+
const scanning_1 = require("@shroud-fi/scanning");
|
|
53
|
+
const transport_1 = require("@shroud-fi/transport");
|
|
54
|
+
const errors_js_1 = require("./errors.js");
|
|
55
|
+
/**
|
|
56
|
+
* ETH zero-address sentinel — some callers may pass this to indicate "ETH" on
|
|
57
|
+
* the sweep dispatch. Kept beside the canonical ETH_SENTINEL constant.
|
|
58
|
+
*/
|
|
59
|
+
const ZERO_ADDRESS_ETH = '0x0000000000000000000000000000000000000000';
|
|
60
|
+
/**
|
|
61
|
+
* The ShroudAgent class. Wraps an EIP-5564 identity + the four ShroudFi
|
|
62
|
+
* packages behind a single ergonomic surface.
|
|
63
|
+
*
|
|
64
|
+
* Constructor is exported but typically reached through `createShroudAgent` or
|
|
65
|
+
* `createShroudAgentFromBrowserWallet`. Direct construction is supported for
|
|
66
|
+
* tests that need to inject a mock scanner backend.
|
|
67
|
+
*/
|
|
68
|
+
class ShroudAgent {
|
|
69
|
+
identity;
|
|
70
|
+
metaAddress;
|
|
71
|
+
metaAddressEncoded;
|
|
72
|
+
transport;
|
|
73
|
+
stealthContract;
|
|
74
|
+
finality;
|
|
75
|
+
startBlock;
|
|
76
|
+
announcer;
|
|
77
|
+
backendOverride;
|
|
78
|
+
autoRegister;
|
|
79
|
+
scanner;
|
|
80
|
+
isStarted = false;
|
|
81
|
+
controller;
|
|
82
|
+
constructor(opts) {
|
|
83
|
+
this.identity = opts.identity;
|
|
84
|
+
this.metaAddress = opts.identity.metaAddress;
|
|
85
|
+
this.metaAddressEncoded = (0, core_1.encodeMetaAddress)(opts.identity.metaAddress);
|
|
86
|
+
this.transport = opts.transport;
|
|
87
|
+
this.stealthContract = opts.stealthContract;
|
|
88
|
+
this.startBlock = opts.startBlock;
|
|
89
|
+
this.finality = opts.finality ?? 'safe';
|
|
90
|
+
this.announcer = opts.announcer;
|
|
91
|
+
this.backendOverride = opts.backend;
|
|
92
|
+
this.autoRegister = opts.autoRegister ?? false;
|
|
93
|
+
// Scanner is built lazily in start() — see H8 in docs/debug/P4-STEP3-STALL.md.
|
|
94
|
+
// Reusing one Scanner across start/stop/start cycles silently dropped events
|
|
95
|
+
// because Scanner.close() permanently sets its `closed` flag.
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Build a fresh Scanner with this agent's config. Called from start() so
|
|
99
|
+
* each lifecycle gets its own scanner instance (a closed scanner can't be
|
|
100
|
+
* reopened — see H8).
|
|
101
|
+
*/
|
|
102
|
+
buildScanner() {
|
|
103
|
+
return (0, scanning_1.createScanner)({
|
|
104
|
+
transport: this.transport,
|
|
105
|
+
scanningKey: this.identity.keys.scanningKey,
|
|
106
|
+
spendingKey: this.identity.keys.spendingKey,
|
|
107
|
+
startBlock: this.startBlock,
|
|
108
|
+
finality: this.finality,
|
|
109
|
+
...(this.announcer !== undefined ? { contractAddress: this.announcer } : {}),
|
|
110
|
+
...(this.backendOverride !== undefined ? { backend: this.backendOverride } : {}),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Start receiving detected payments.
|
|
115
|
+
*
|
|
116
|
+
* Returns an AsyncIterable; consumer drives via `for await`. If
|
|
117
|
+
* `opts.backfillFrom` is set, the first phase yields scanRange results,
|
|
118
|
+
* then transitions to live watch().
|
|
119
|
+
*
|
|
120
|
+
* @throws AgentAlreadyStartedError if called twice without stop().
|
|
121
|
+
*/
|
|
122
|
+
start(opts) {
|
|
123
|
+
if (this.isStarted) {
|
|
124
|
+
throw new errors_js_1.AgentAlreadyStartedError();
|
|
125
|
+
}
|
|
126
|
+
this.isStarted = true;
|
|
127
|
+
const controller = new AbortController();
|
|
128
|
+
this.controller = controller;
|
|
129
|
+
// Forward external signal to internal controller.
|
|
130
|
+
const externalSignal = opts?.signal;
|
|
131
|
+
if (externalSignal !== undefined) {
|
|
132
|
+
if (externalSignal.aborted) {
|
|
133
|
+
controller.abort();
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
externalSignal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Build a fresh scanner for THIS start() cycle. Reusing the same scanner
|
|
140
|
+
// across start/stop/start would silently drop events because Scanner.close()
|
|
141
|
+
// sets a permanent `closed` flag inside detector.ts. See H8.
|
|
142
|
+
const scanner = this.buildScanner();
|
|
143
|
+
this.scanner = scanner;
|
|
144
|
+
const finality = this.finality;
|
|
145
|
+
const transport = this.transport;
|
|
146
|
+
const backfillFrom = opts?.backfillFrom;
|
|
147
|
+
const resetStarted = () => {
|
|
148
|
+
this.isStarted = false;
|
|
149
|
+
};
|
|
150
|
+
// EAGERLY open the live watch subscription before this method returns.
|
|
151
|
+
// `scanner.watch()` is lazy — the backend.watchAnnouncements registration
|
|
152
|
+
// only fires when [Symbol.asyncIterator]() is called. If we left this to
|
|
153
|
+
// the generator body below (which runs on first iter.next()), a caller
|
|
154
|
+
// pattern like:
|
|
155
|
+
// const iter = agent.start();
|
|
156
|
+
// await agent.send(...) // tx mines before we subscribe
|
|
157
|
+
// for await (const d of iter) ... // never sees the event
|
|
158
|
+
// would race and miss the announcement. Subscribing here closes the gap.
|
|
159
|
+
const watchIterator = scanner.watch(controller.signal)[Symbol.asyncIterator]();
|
|
160
|
+
return {
|
|
161
|
+
[Symbol.asyncIterator]: async function* () {
|
|
162
|
+
try {
|
|
163
|
+
if (backfillFrom !== undefined) {
|
|
164
|
+
// Determine the latest block we can backfill to. The tag mirrors
|
|
165
|
+
// the configured finality so unfinalized history is not yielded
|
|
166
|
+
// for 'safe' / 'finalized' callers. 'unsafe' callers (and chains
|
|
167
|
+
// that don't track 'safe'/'finalized' — e.g. Anvil) use 'latest'
|
|
168
|
+
// so the backfill reaches the actual head.
|
|
169
|
+
const tag = finality === 'finalized'
|
|
170
|
+
? 'finalized'
|
|
171
|
+
: finality === 'safe'
|
|
172
|
+
? 'safe'
|
|
173
|
+
: 'latest';
|
|
174
|
+
const latestBlock = await transport.publicClient.getBlock({
|
|
175
|
+
blockTag: tag,
|
|
176
|
+
});
|
|
177
|
+
const latestSafe = latestBlock.number ?? backfillFrom;
|
|
178
|
+
if (latestSafe >= backfillFrom) {
|
|
179
|
+
for await (const detection of scanner.scanRange(backfillFrom, latestSafe, controller.signal)) {
|
|
180
|
+
if (controller.signal.aborted)
|
|
181
|
+
return;
|
|
182
|
+
yield detection;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Drain the live watch iterator we eagerly opened above.
|
|
187
|
+
while (true) {
|
|
188
|
+
if (controller.signal.aborted)
|
|
189
|
+
return;
|
|
190
|
+
const r = await watchIterator.next();
|
|
191
|
+
if (r.done)
|
|
192
|
+
return;
|
|
193
|
+
yield r.value;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
// Tear down the eagerly-opened watch iterator so scanner.watchActive
|
|
198
|
+
// resets and a follow-up start() can resubscribe.
|
|
199
|
+
try {
|
|
200
|
+
await watchIterator.return?.({
|
|
201
|
+
value: undefined,
|
|
202
|
+
done: true,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// close path must never throw out of finally.
|
|
207
|
+
}
|
|
208
|
+
resetStarted();
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Stop the agent. Aborts the internal signal, closes the current scanner,
|
|
215
|
+
* and clears the reference so the next start() builds a fresh one.
|
|
216
|
+
* Idempotent — safe to call multiple times or before start().
|
|
217
|
+
*/
|
|
218
|
+
stop() {
|
|
219
|
+
if (this.controller !== undefined) {
|
|
220
|
+
try {
|
|
221
|
+
this.controller.abort();
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
// Swallow — stop must not throw.
|
|
225
|
+
}
|
|
226
|
+
this.controller = undefined;
|
|
227
|
+
}
|
|
228
|
+
if (this.scanner !== undefined) {
|
|
229
|
+
try {
|
|
230
|
+
this.scanner.close();
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
// Swallow — stop must not throw.
|
|
234
|
+
}
|
|
235
|
+
this.scanner = undefined;
|
|
236
|
+
}
|
|
237
|
+
this.isStarted = false;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Send native ETH to a recipient stealth meta-address.
|
|
241
|
+
*
|
|
242
|
+
* Accepts either a parsed StealthMetaAddress or the encoded string form
|
|
243
|
+
* (`st:base:0x...`). String form is decoded via decodeMetaAddress before use.
|
|
244
|
+
*/
|
|
245
|
+
async send(to, valueWei) {
|
|
246
|
+
const meta = typeof to === 'string' ? (0, core_1.decodeMetaAddress)(to) : to;
|
|
247
|
+
return (0, payments_1.sendETHPayment)(this.transport, this.stealthContract, meta, valueWei);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Send an ERC-20 token to a recipient stealth meta-address.
|
|
251
|
+
*
|
|
252
|
+
* Caller MUST have already approved the stealth contract to spend the token
|
|
253
|
+
* — v1 does not include an approve flow.
|
|
254
|
+
*/
|
|
255
|
+
async sendERC20(to, token, amount) {
|
|
256
|
+
const meta = typeof to === 'string' ? (0, core_1.decodeMetaAddress)(to) : to;
|
|
257
|
+
return (0, payments_1.sendERC20Payment)(this.transport, this.stealthContract, meta, token, amount);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* M3 — read the canonical ERC-6538 registry and return whether the agent's
|
|
261
|
+
* registrant wallet has a non-empty stealth meta-address on file for
|
|
262
|
+
* scheme 1 (the ShroudFi default).
|
|
263
|
+
*
|
|
264
|
+
* The registrant is keyed by `transport.walletClient.account.address` —
|
|
265
|
+
* the EOA that would sign the `registerKeys` transaction. Read-only
|
|
266
|
+
* transports (no walletClient or no account) cannot be registered and
|
|
267
|
+
* therefore return `false`.
|
|
268
|
+
*
|
|
269
|
+
* No side effects, no gas. Safe to call before every send.
|
|
270
|
+
*/
|
|
271
|
+
async isRegistered() {
|
|
272
|
+
const walletClient = this.transport.walletClient;
|
|
273
|
+
if (walletClient === undefined)
|
|
274
|
+
return false;
|
|
275
|
+
const account = walletClient.account;
|
|
276
|
+
if (account === undefined)
|
|
277
|
+
return false;
|
|
278
|
+
const registrant = account.address;
|
|
279
|
+
const raw = (await this.transport.publicClient.readContract({
|
|
280
|
+
address: transport_1.ERC6538_REGISTRY,
|
|
281
|
+
abi: transport_1.ERC6538RegistryAbi,
|
|
282
|
+
functionName: 'stealthMetaAddressOf',
|
|
283
|
+
args: [registrant, REGISTRY_SCHEME_ID],
|
|
284
|
+
}));
|
|
285
|
+
// viem returns '0x' for empty bytes. Anything longer is a registration.
|
|
286
|
+
return raw !== '0x' && raw.length > 2;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* M3 — publish the agent's stealth meta-address (33-byte spending pubkey
|
|
290
|
+
* concatenated with 33-byte viewing pubkey) into the canonical ERC-6538
|
|
291
|
+
* registry under scheme 1.
|
|
292
|
+
*
|
|
293
|
+
* Throws `AlreadyRegisteredError` if the registrant wallet already has a
|
|
294
|
+
* non-empty entry. Throws `RegistrationRequiresWalletError` if the
|
|
295
|
+
* transport has no walletClient with an account.
|
|
296
|
+
*
|
|
297
|
+
* Privacy: only the agent's PUBLIC keys cross the wire (these are the
|
|
298
|
+
* same bytes that already live in `metaAddressEncoded`). The spending
|
|
299
|
+
* private key is never touched by this path.
|
|
300
|
+
*
|
|
301
|
+
* @returns the transaction hash of the `registerKeys` call.
|
|
302
|
+
*/
|
|
303
|
+
async register() {
|
|
304
|
+
const walletClient = this.transport.walletClient;
|
|
305
|
+
if (walletClient === undefined) {
|
|
306
|
+
throw new errors_js_1.RegistrationRequiresWalletError();
|
|
307
|
+
}
|
|
308
|
+
const account = walletClient.account;
|
|
309
|
+
if (account === undefined) {
|
|
310
|
+
throw new errors_js_1.RegistrationRequiresWalletError();
|
|
311
|
+
}
|
|
312
|
+
// Fail fast if already registered. This both saves the operator a gas
|
|
313
|
+
// payment and keeps `register()` honest: the only way to overwrite an
|
|
314
|
+
// existing entry on the canonical registry is to bump the per-scheme
|
|
315
|
+
// nonce, which is out of scope for v1.
|
|
316
|
+
if (await this.isRegistered()) {
|
|
317
|
+
throw new errors_js_1.AlreadyRegisteredError();
|
|
318
|
+
}
|
|
319
|
+
// Build the 66-byte payload: 33 spending pubkey || 33 viewing pubkey.
|
|
320
|
+
// The registrar contract layout is confirmed by
|
|
321
|
+
// contracts/src/ShroudFiRegistrar.sol (each pubkey is a compressed
|
|
322
|
+
// secp256k1 point — 33 bytes, prefix 0x02 / 0x03).
|
|
323
|
+
const payload = (0, viem_1.concatBytes)([
|
|
324
|
+
this.metaAddress.spendingPubKey,
|
|
325
|
+
this.metaAddress.viewingPubKey,
|
|
326
|
+
]);
|
|
327
|
+
const stealthMetaAddress = (0, viem_1.bytesToHex)(payload);
|
|
328
|
+
const writeArgs = {
|
|
329
|
+
address: transport_1.ERC6538_REGISTRY,
|
|
330
|
+
abi: transport_1.ERC6538RegistryAbi,
|
|
331
|
+
functionName: 'registerKeys',
|
|
332
|
+
args: [REGISTRY_SCHEME_ID, stealthMetaAddress],
|
|
333
|
+
account,
|
|
334
|
+
chain: this.transport.chain,
|
|
335
|
+
};
|
|
336
|
+
// viem's writeContract has a complex union signature — cast at the
|
|
337
|
+
// boundary, mirroring the pattern used in @shroud-fi/payments.
|
|
338
|
+
const txHash = (await walletClient.writeContract(writeArgs));
|
|
339
|
+
return txHash;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* M3 — composite, idempotent registration helper.
|
|
343
|
+
*
|
|
344
|
+
* Returns `{ registered: false }` if the agent is already registered
|
|
345
|
+
* (no tx broadcast). Otherwise returns `{ registered: true, txHash }`
|
|
346
|
+
* with the registration tx hash.
|
|
347
|
+
*
|
|
348
|
+
* Safe to call before every send — the check is a single eth_call.
|
|
349
|
+
*/
|
|
350
|
+
async ensureRegistered() {
|
|
351
|
+
if (await this.isRegistered()) {
|
|
352
|
+
return { registered: false };
|
|
353
|
+
}
|
|
354
|
+
const txHash = await this.register();
|
|
355
|
+
return { registered: true, txHash };
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* M4 — ergonomic helper: pay an EVM wallet by its plain address, no
|
|
359
|
+
* meta-address handling on the caller side.
|
|
360
|
+
*
|
|
361
|
+
* Delegates to `sendToWallet` from `@shroud-fi/payments` which does the
|
|
362
|
+
* ERC-6538 registry lookup, decodes the recipient's stealth meta-address,
|
|
363
|
+
* and dispatches `sendETHPayment` or `sendERC20Payment` as appropriate.
|
|
364
|
+
*
|
|
365
|
+
* If the agent was constructed with `autoRegister: true`, this method
|
|
366
|
+
* calls `ensureRegistered` BEFORE dispatching the payment so the agent
|
|
367
|
+
* itself becomes reachable as a stealth-payment recipient the first time
|
|
368
|
+
* it pays someone. A failure inside `ensureRegistered` is wrapped in
|
|
369
|
+
* `AutoRegistrationFailedError` so callers can distinguish setup failures
|
|
370
|
+
* from payment failures via the `cause` chain.
|
|
371
|
+
*
|
|
372
|
+
* Privacy: the recipient wallet address never appears in error messages
|
|
373
|
+
* — `RecipientNotOnboardedError.wallet` exposes it on a structured field
|
|
374
|
+
* for callers that need to show a UI hint.
|
|
375
|
+
*/
|
|
376
|
+
async sendToWallet(recipientWallet, asset, amount, options) {
|
|
377
|
+
if (this.autoRegister) {
|
|
378
|
+
try {
|
|
379
|
+
await this.ensureRegistered();
|
|
380
|
+
}
|
|
381
|
+
catch (err) {
|
|
382
|
+
// Preserve the underlying cause so callers can introspect (e.g.,
|
|
383
|
+
// tell apart a missing walletClient from an RPC error). The wrapper
|
|
384
|
+
// message itself is privacy-safe — see AutoRegistrationFailedError.
|
|
385
|
+
throw new errors_js_1.AutoRegistrationFailedError(err);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return (0, payments_1.sendToWallet)(this.transport, recipientWallet, asset, amount, options);
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Sweep funds from a detected stealth address to a destination.
|
|
392
|
+
*
|
|
393
|
+
* If `opts.token` is undefined or matches the ETH sentinel (or zero address)
|
|
394
|
+
* the agent dispatches to sweepETH; otherwise it dispatches to sweepERC20
|
|
395
|
+
* with the given token.
|
|
396
|
+
*
|
|
397
|
+
* Direct sweep (the default path) requires the stealth address to hold
|
|
398
|
+
* enough ETH to pay its own gas. When the stealth holds zero ETH set
|
|
399
|
+
* `opts.viaRelayer = true`:
|
|
400
|
+
* - ERC-20 → routed to {@link sweepViaRelayer} (Gelato or self-host).
|
|
401
|
+
* - ETH → routed to {@link sweepEthViaRelayer} via EIP-7702 (P5.1).
|
|
402
|
+
* Requires `opts.ethRelayerOptions.selfHostEndpoint` since there's no
|
|
403
|
+
* third-party fallback for the 7702 path.
|
|
404
|
+
*/
|
|
405
|
+
async sweep(detection, destination, opts) {
|
|
406
|
+
const token = opts?.token;
|
|
407
|
+
const isEth = token === undefined ||
|
|
408
|
+
token === ZERO_ADDRESS_ETH ||
|
|
409
|
+
token.toLowerCase() === payments_1.ETH_SENTINEL.toLowerCase();
|
|
410
|
+
if (opts?.viaRelayer === true) {
|
|
411
|
+
if (isEth) {
|
|
412
|
+
if (opts.ethRelayerOptions === undefined) {
|
|
413
|
+
throw new errors_js_1.EthRelayerEndpointRequiredError();
|
|
414
|
+
}
|
|
415
|
+
return this.sweepEthViaRelayer(detection, destination, {
|
|
416
|
+
...(opts.ethRelayerContract !== undefined
|
|
417
|
+
? { ethRelayerContract: opts.ethRelayerContract }
|
|
418
|
+
: {}),
|
|
419
|
+
relayerOptions: opts.ethRelayerOptions,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
return this.sweepViaRelayer(detection, destination, {
|
|
423
|
+
token,
|
|
424
|
+
...(opts.relayerContract !== undefined ? { relayerContract: opts.relayerContract } : {}),
|
|
425
|
+
...(opts.relayerOptions !== undefined ? { relayerOptions: opts.relayerOptions } : {}),
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
if (isEth) {
|
|
429
|
+
const swept = await (0, payments_1.sweepETH)(this.transport, detection.stealthPrivateKey, destination);
|
|
430
|
+
return { swept, detection };
|
|
431
|
+
}
|
|
432
|
+
const swept = await (0, payments_1.sweepERC20)(this.transport, detection.stealthPrivateKey, token, destination);
|
|
433
|
+
return { swept, detection };
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Gasless ERC-20 sweep via the ShroudFiRelayer + Gelato 1Balance.
|
|
437
|
+
*
|
|
438
|
+
* Lazy-imports `@shroud-fi/relayer` so the agent-runtime bundle stays small
|
|
439
|
+
* for callers that never use the relayer path. The Gelato SDK + axios + ws
|
|
440
|
+
* deps only land in the bundle when this method is actually called.
|
|
441
|
+
*
|
|
442
|
+
* Relayer contract resolution order:
|
|
443
|
+
* 1. `opts.relayerContract` if provided
|
|
444
|
+
* 2. `getRelayer(transport.chain.id)` lookup from `@shroud-fi/transport`
|
|
445
|
+
* 3. Throw `RelayerContractNotConfiguredError`
|
|
446
|
+
*
|
|
447
|
+
* @throws RelayerContractNotConfiguredError if no relayer is resolvable.
|
|
448
|
+
* @throws RelayerNotAvailableForETHError if the agent itself ever invokes
|
|
449
|
+
* this with an ETH-like token (the dispatch in `sweep()` already
|
|
450
|
+
* guards, this guards direct callers too).
|
|
451
|
+
*/
|
|
452
|
+
async sweepViaRelayer(detection, destination, opts) {
|
|
453
|
+
const { token } = opts;
|
|
454
|
+
const isEth = token === ZERO_ADDRESS_ETH ||
|
|
455
|
+
token.toLowerCase() === payments_1.ETH_SENTINEL.toLowerCase();
|
|
456
|
+
if (isEth) {
|
|
457
|
+
// Direct callers asking for ERC-20 relayer with the ETH sentinel need
|
|
458
|
+
// to use sweepEthViaRelayer instead.
|
|
459
|
+
throw new errors_js_1.EthRelayerEndpointRequiredError();
|
|
460
|
+
}
|
|
461
|
+
const chainId = this.transport.chain.id;
|
|
462
|
+
const relayerContract = opts.relayerContract ?? (0, transport_1.getRelayer)(chainId);
|
|
463
|
+
if (relayerContract === undefined) {
|
|
464
|
+
throw new errors_js_1.RelayerContractNotConfiguredError();
|
|
465
|
+
}
|
|
466
|
+
// Lazy dynamic import — keeps the agent-runtime ESM bundle small for
|
|
467
|
+
// consumers that never call sweepViaRelayer. The Gelato SDK + transitive
|
|
468
|
+
// deps (axios, ws, isomorphic-ws) only enter the bundle on demand.
|
|
469
|
+
const relayerMod = await Promise.resolve().then(() => __importStar(require('@shroud-fi/relayer')));
|
|
470
|
+
// Self-host path: skip Gelato. Sign the permit locally, POST the request
|
|
471
|
+
// to the operator-controlled relayer service, return a receipt shaped
|
|
472
|
+
// like the Gelato one.
|
|
473
|
+
const selfHostEndpoint = opts.relayerOptions?.selfHostEndpoint;
|
|
474
|
+
if (typeof selfHostEndpoint === 'string' && selfHostEndpoint.length > 0) {
|
|
475
|
+
const swept = await this.sweepViaSelfHost(relayerMod.signEip2612Permit, detection, destination, token, relayerContract, selfHostEndpoint, opts.relayerOptions);
|
|
476
|
+
return { swept, detection };
|
|
477
|
+
}
|
|
478
|
+
const swept = await relayerMod.relayedSweepERC20(this.transport, detection.stealthPrivateKey, token, destination, relayerContract, opts.relayerOptions);
|
|
479
|
+
return { swept, detection };
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Self-host dispatch: sign the EIP-2612 permit, POST to the operator's
|
|
483
|
+
* service, return a Gelato-shaped receipt.
|
|
484
|
+
*
|
|
485
|
+
* Privacy: only the (public) permit signature + stealth address + token
|
|
486
|
+
* + destination cross the wire. The stealth private key never leaves the
|
|
487
|
+
* client.
|
|
488
|
+
*/
|
|
489
|
+
async sweepViaSelfHost(signEip2612Permit, detection, destination, token, relayerContract, endpoint, options) {
|
|
490
|
+
// 1. Read the stealth balance.
|
|
491
|
+
const stealthAddress = detection.stealthAddress;
|
|
492
|
+
const balance = (await this.transport.publicClient.readContract({
|
|
493
|
+
address: token,
|
|
494
|
+
abi: BALANCE_OF_ABI,
|
|
495
|
+
functionName: 'balanceOf',
|
|
496
|
+
args: [stealthAddress],
|
|
497
|
+
}));
|
|
498
|
+
// 2. Compute the permit deadline.
|
|
499
|
+
const lifetime = options?.deadlineSecs ?? 1800n;
|
|
500
|
+
const latestBlock = await this.transport.publicClient.getBlock();
|
|
501
|
+
const deadline = latestBlock.timestamp + lifetime;
|
|
502
|
+
// 3. Sign the permit (the only place the stealth private key is consumed).
|
|
503
|
+
const permit = await signEip2612Permit(this.transport, detection.stealthPrivateKey, token, relayerContract, balance, deadline);
|
|
504
|
+
// 4. POST to the self-host service.
|
|
505
|
+
const body = {
|
|
506
|
+
token,
|
|
507
|
+
destination,
|
|
508
|
+
stealthAddress,
|
|
509
|
+
deadline: permit.deadline.toString(),
|
|
510
|
+
v: permit.v,
|
|
511
|
+
r: permit.r,
|
|
512
|
+
s: permit.s,
|
|
513
|
+
};
|
|
514
|
+
const fetchInit = {
|
|
515
|
+
method: 'POST',
|
|
516
|
+
headers: { 'content-type': 'application/json' },
|
|
517
|
+
body: JSON.stringify(body),
|
|
518
|
+
};
|
|
519
|
+
if (options?.signal !== undefined) {
|
|
520
|
+
fetchInit.signal = options.signal;
|
|
521
|
+
}
|
|
522
|
+
const response = await fetch(`${endpoint.replace(/\/$/, '')}/relay`, fetchInit);
|
|
523
|
+
const json = (await response.json());
|
|
524
|
+
if (!json.ok) {
|
|
525
|
+
throw new Error(json.error);
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
taskId: `self-host:${json.txHash}`,
|
|
529
|
+
txHash: json.txHash,
|
|
530
|
+
status: 'success',
|
|
531
|
+
relayerContract,
|
|
532
|
+
token,
|
|
533
|
+
destination,
|
|
534
|
+
blockNumber: BigInt(json.blockNumber),
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* P5.1 — gasless ETH sweep via EIP-7702 + self-host relayer service.
|
|
539
|
+
*
|
|
540
|
+
* Dispatch order for the ShroudFiEthRelayer contract address:
|
|
541
|
+
* 1. `opts.ethRelayerContract` if provided
|
|
542
|
+
* 2. `getEthRelayer(transport.chain.id)` lookup
|
|
543
|
+
* 3. Throw EthRelayerContractNotConfiguredError
|
|
544
|
+
*
|
|
545
|
+
* Lazy-imports @shroud-fi/relayer so the agent-runtime bundle stays small
|
|
546
|
+
* for callers that never touch the gasless path.
|
|
547
|
+
*
|
|
548
|
+
* @throws EthRelayerContractNotConfiguredError if no contract is resolvable.
|
|
549
|
+
* @throws EthRelayerEndpointRequiredError if no self-host endpoint is provided.
|
|
550
|
+
*/
|
|
551
|
+
async sweepEthViaRelayer(detection, destination, opts) {
|
|
552
|
+
const chainId = this.transport.chain.id;
|
|
553
|
+
const ethRelayerContract = opts.ethRelayerContract ?? (0, transport_1.getEthRelayer)(chainId);
|
|
554
|
+
if (ethRelayerContract === undefined) {
|
|
555
|
+
throw new errors_js_1.EthRelayerContractNotConfiguredError();
|
|
556
|
+
}
|
|
557
|
+
const endpoint = opts.relayerOptions.selfHostEndpoint;
|
|
558
|
+
if (typeof endpoint !== 'string' || endpoint.length === 0) {
|
|
559
|
+
throw new errors_js_1.EthRelayerEndpointRequiredError();
|
|
560
|
+
}
|
|
561
|
+
// Lazy dynamic import keeps the agent-runtime ESM bundle small.
|
|
562
|
+
const relayerMod = await Promise.resolve().then(() => __importStar(require('@shroud-fi/relayer')));
|
|
563
|
+
const passOptions = {};
|
|
564
|
+
if (opts.relayerOptions.deadlineSecs !== undefined) {
|
|
565
|
+
passOptions.deadlineSecs = opts.relayerOptions.deadlineSecs;
|
|
566
|
+
}
|
|
567
|
+
if (opts.relayerOptions.pollTimeoutMs !== undefined) {
|
|
568
|
+
passOptions.pollTimeoutMs = opts.relayerOptions.pollTimeoutMs;
|
|
569
|
+
}
|
|
570
|
+
if (opts.relayerOptions.signal !== undefined) {
|
|
571
|
+
passOptions.signal = opts.relayerOptions.signal;
|
|
572
|
+
}
|
|
573
|
+
const swept = await relayerMod.relayedSweepETH(this.transport, detection.stealthPrivateKey, destination, ethRelayerContract, endpoint, passOptions);
|
|
574
|
+
return { swept, detection };
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
exports.ShroudAgent = ShroudAgent;
|
|
578
|
+
// Minimal balanceOf ABI for the self-host pre-flight balance read.
|
|
579
|
+
const BALANCE_OF_ABI = [
|
|
580
|
+
{
|
|
581
|
+
type: 'function',
|
|
582
|
+
name: 'balanceOf',
|
|
583
|
+
stateMutability: 'view',
|
|
584
|
+
inputs: [{ name: 'account', type: 'address' }],
|
|
585
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
586
|
+
},
|
|
587
|
+
];
|
|
588
|
+
/**
|
|
589
|
+
* ERC-6538 scheme identifier for secp256k1 stealth addresses with view-tag
|
|
590
|
+
* filtering (per EIP-5564). Kept as a `bigint` because the registry signature
|
|
591
|
+
* is `uint256 schemeId`. Mirrors `SCHEME_ID` in @shroud-fi/payments.
|
|
592
|
+
*/
|
|
593
|
+
const REGISTRY_SCHEME_ID = 1n;
|
|
594
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createShroudAgentFromBrowserWallet — browser / EIP-1193 wallet entry point.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Ask the wallet to sign BROWSER_KEY_MESSAGE via personal_sign.
|
|
6
|
+
* 2. HKDF the signature bytes to a deterministic 32-byte master seed.
|
|
7
|
+
* 3. Delegate to createShroudAgent.
|
|
8
|
+
*
|
|
9
|
+
* The same wallet always produces the same agent identity — no localStorage,
|
|
10
|
+
* no persistence. Bumping BROWSER_KEY_MESSAGE forks the identity space.
|
|
11
|
+
*
|
|
12
|
+
* Privacy invariants:
|
|
13
|
+
* - No console.* anywhere.
|
|
14
|
+
* - The signature bytes never appear in error messages or stack frames.
|
|
15
|
+
* - The derived master seed is passed to createShroudAgent and immediately
|
|
16
|
+
* consumed by createAgentIdentity; no caller-visible reference is retained.
|
|
17
|
+
*/
|
|
18
|
+
import type { Address } from 'viem';
|
|
19
|
+
import type { FinalityLevel } from '@shroud-fi/scanning';
|
|
20
|
+
import type { ShroudFiTransport } from '@shroud-fi/transport';
|
|
21
|
+
import type { ShroudAgent } from './agent.js';
|
|
22
|
+
import type { BrowserWalletAdapter } from './types.js';
|
|
23
|
+
export { BROWSER_KEY_MESSAGE, HKDF_BROWSER_SALT, HKDF_BROWSER_INFO, } from './constants.js';
|
|
24
|
+
export interface CreateShroudAgentFromBrowserWalletArgs {
|
|
25
|
+
readonly wallet: BrowserWalletAdapter;
|
|
26
|
+
readonly transport: ShroudFiTransport;
|
|
27
|
+
readonly stealthContract?: Address;
|
|
28
|
+
readonly startBlock: bigint;
|
|
29
|
+
readonly finality?: FinalityLevel;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build a ShroudAgent from a browser wallet adapter (e.g. MetaMask).
|
|
33
|
+
*
|
|
34
|
+
* @throws MissingBrowserWalletError if the adapter is missing required fields.
|
|
35
|
+
* @throws BrowserWalletSignatureRejectedError if the signature request is rejected.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createShroudAgentFromBrowserWallet(args: CreateShroudAgentFromBrowserWalletArgs): Promise<ShroudAgent>;
|
|
38
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAGpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAW9C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,sCAAsC;IACrD,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;CACnC;AAoBD;;;;;GAKG;AACH,wBAAsB,kCAAkC,CACtD,IAAI,EAAE,sCAAsC,GAC3C,OAAO,CAAC,WAAW,CAAC,CAgDtB"}
|