@shroud-fi/relayer 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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -0
  3. package/dist/cjs/constants.d.ts +41 -0
  4. package/dist/cjs/constants.d.ts.map +1 -0
  5. package/dist/cjs/constants.js +49 -0
  6. package/dist/cjs/constants.js.map +1 -0
  7. package/dist/cjs/errors.d.ts +56 -0
  8. package/dist/cjs/errors.d.ts.map +1 -0
  9. package/dist/cjs/errors.js +85 -0
  10. package/dist/cjs/errors.js.map +1 -0
  11. package/dist/cjs/eth-sweep.d.ts +36 -0
  12. package/dist/cjs/eth-sweep.d.ts.map +1 -0
  13. package/dist/cjs/eth-sweep.js +193 -0
  14. package/dist/cjs/eth-sweep.js.map +1 -0
  15. package/dist/cjs/gelato-adapter.d.ts +69 -0
  16. package/dist/cjs/gelato-adapter.d.ts.map +1 -0
  17. package/dist/cjs/gelato-adapter.js +209 -0
  18. package/dist/cjs/gelato-adapter.js.map +1 -0
  19. package/dist/cjs/index.d.ts +7 -0
  20. package/dist/cjs/index.d.ts.map +1 -0
  21. package/dist/cjs/index.js +26 -0
  22. package/dist/cjs/index.js.map +1 -0
  23. package/dist/cjs/package.json +1 -0
  24. package/dist/cjs/relayer.d.ts +23 -0
  25. package/dist/cjs/relayer.d.ts.map +1 -0
  26. package/dist/cjs/relayer.js +149 -0
  27. package/dist/cjs/relayer.js.map +1 -0
  28. package/dist/cjs/signing.d.ts +24 -0
  29. package/dist/cjs/signing.d.ts.map +1 -0
  30. package/dist/cjs/signing.js +155 -0
  31. package/dist/cjs/signing.js.map +1 -0
  32. package/dist/cjs/types.d.ts +88 -0
  33. package/dist/cjs/types.d.ts.map +1 -0
  34. package/dist/cjs/types.js +3 -0
  35. package/dist/cjs/types.js.map +1 -0
  36. package/dist/esm/constants.d.ts +41 -0
  37. package/dist/esm/constants.d.ts.map +1 -0
  38. package/dist/esm/constants.js +46 -0
  39. package/dist/esm/constants.js.map +1 -0
  40. package/dist/esm/errors.d.ts +56 -0
  41. package/dist/esm/errors.d.ts.map +1 -0
  42. package/dist/esm/errors.js +74 -0
  43. package/dist/esm/errors.js.map +1 -0
  44. package/dist/esm/eth-sweep.d.ts +36 -0
  45. package/dist/esm/eth-sweep.d.ts.map +1 -0
  46. package/dist/esm/eth-sweep.js +190 -0
  47. package/dist/esm/eth-sweep.js.map +1 -0
  48. package/dist/esm/gelato-adapter.d.ts +69 -0
  49. package/dist/esm/gelato-adapter.d.ts.map +1 -0
  50. package/dist/esm/gelato-adapter.js +205 -0
  51. package/dist/esm/gelato-adapter.js.map +1 -0
  52. package/dist/esm/index.d.ts +7 -0
  53. package/dist/esm/index.d.ts.map +1 -0
  54. package/dist/esm/index.js +6 -0
  55. package/dist/esm/index.js.map +1 -0
  56. package/dist/esm/relayer.d.ts +23 -0
  57. package/dist/esm/relayer.d.ts.map +1 -0
  58. package/dist/esm/relayer.js +146 -0
  59. package/dist/esm/relayer.js.map +1 -0
  60. package/dist/esm/signing.d.ts +24 -0
  61. package/dist/esm/signing.d.ts.map +1 -0
  62. package/dist/esm/signing.js +152 -0
  63. package/dist/esm/signing.js.map +1 -0
  64. package/dist/esm/types.d.ts +88 -0
  65. package/dist/esm/types.d.ts.map +1 -0
  66. package/dist/esm/types.js +2 -0
  67. package/dist/esm/types.js.map +1 -0
  68. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  69. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  70. package/dist/tsconfig.tsbuildinfo +1 -0
  71. package/package.json +64 -0
@@ -0,0 +1,205 @@
1
+ import { createWalletClient, http } from 'viem';
2
+ import { privateKeyToAccount } from 'viem/accounts';
3
+ import { GelatoRelay, TaskState } from '@gelatonetwork/relay-sdk-viem';
4
+ import { GELATO_TASK_STATUS_URL, } from './constants.js';
5
+ import { RelayerExecutionFailedError, RelayerSubmissionError, RelayerTimeoutError, } from './errors.js';
6
+ // Lazily instantiate a single relay client per process. Stateless wrt
7
+ // chain/key so safe to share.
8
+ let _relay;
9
+ function relay() {
10
+ if (_relay === undefined) {
11
+ _relay = new GelatoRelay();
12
+ }
13
+ return _relay;
14
+ }
15
+ /**
16
+ * Submit a sponsored ERC-2771 metaTx via Gelato 1Balance.
17
+ *
18
+ * Returns the Gelato `taskId`. Caller is responsible for polling.
19
+ */
20
+ export async function submitSponsoredCallErc2771(p) {
21
+ const account = privateKeyToAccount(p.userPrivateKey);
22
+ // Build an ephemeral walletClient. The Gelato SDK uses it to sign the
23
+ // ERC-2771 typed data. We deliberately avoid persisting it.
24
+ const wallet = createWalletClient({
25
+ account,
26
+ chain: p.chain,
27
+ transport: p.rpcUrl !== undefined ? http(p.rpcUrl) : http(),
28
+ });
29
+ const request = {
30
+ chainId: BigInt(p.chainId),
31
+ target: p.target,
32
+ data: p.data,
33
+ user: p.user,
34
+ isConcurrent: false,
35
+ };
36
+ try {
37
+ const response = await relay().sponsoredCallERC2771(request, wallet, p.apiKey ?? '');
38
+ return response.taskId;
39
+ }
40
+ catch (err) {
41
+ // Surface only the upstream error class name; never the message
42
+ // (may contain calldata bytes, addresses, or RPC quoting).
43
+ const tag = err instanceof Error ? err.name : 'unknown';
44
+ throw new RelayerSubmissionError(tag);
45
+ }
46
+ }
47
+ function mapTaskState(state) {
48
+ switch (state) {
49
+ case TaskState.ExecSuccess:
50
+ case 'ExecSuccess':
51
+ return 'success';
52
+ case TaskState.ExecReverted:
53
+ case 'ExecReverted':
54
+ return 'failed';
55
+ case TaskState.Cancelled:
56
+ case 'Cancelled':
57
+ return 'cancelled';
58
+ case TaskState.CheckPending:
59
+ case TaskState.ExecPending:
60
+ case TaskState.WaitingForConfirmation:
61
+ case 'CheckPending':
62
+ case 'ExecPending':
63
+ case 'WaitingForConfirmation':
64
+ return 'pending';
65
+ default:
66
+ // Treat unknown lifecycle states (e.g. "Blacklisted") as failure.
67
+ return 'failed';
68
+ }
69
+ }
70
+ /**
71
+ * Fetch a single task-status snapshot via the public Gelato HTTP endpoint.
72
+ * Used as fallback if the SDK's getTaskStatus returns undefined.
73
+ */
74
+ async function fetchTaskStatusFallback(taskId, signal) {
75
+ // `fetch` is globally available in Node 18+.
76
+ let resp;
77
+ try {
78
+ const init = signal !== undefined ? { signal } : {};
79
+ resp = await fetch(`${GELATO_TASK_STATUS_URL}${taskId}`, init);
80
+ }
81
+ catch {
82
+ return undefined;
83
+ }
84
+ if (!resp.ok)
85
+ return undefined;
86
+ let body;
87
+ try {
88
+ body = await resp.json();
89
+ }
90
+ catch {
91
+ return undefined;
92
+ }
93
+ // Endpoint returns either { task: {...} } or the bare task object.
94
+ const taskUnknown = body !== null && typeof body === 'object' && 'task' in body
95
+ ? body.task
96
+ : body;
97
+ if (taskUnknown === null || typeof taskUnknown !== 'object')
98
+ return undefined;
99
+ const task = taskUnknown;
100
+ const rawState = task['taskState'];
101
+ if (typeof rawState !== 'string')
102
+ return undefined;
103
+ const state = mapTaskState(rawState);
104
+ const result = {
105
+ taskId,
106
+ state,
107
+ };
108
+ const txHash = task['transactionHash'];
109
+ if (typeof txHash === 'string' && txHash.startsWith('0x')) {
110
+ result.txHash = txHash;
111
+ }
112
+ const blockNumber = task['blockNumber'];
113
+ if (typeof blockNumber === 'number') {
114
+ result.blockNumber = BigInt(blockNumber);
115
+ }
116
+ else if (typeof blockNumber === 'string' && /^\d+$/.test(blockNumber)) {
117
+ result.blockNumber = BigInt(blockNumber);
118
+ }
119
+ const lastErr = task['lastCheckMessage'];
120
+ if (typeof lastErr === 'string' && lastErr.length > 0) {
121
+ result.lastErrorMsg = lastErr;
122
+ }
123
+ return result;
124
+ }
125
+ /**
126
+ * Poll Gelato task status until terminal (success / failed / cancelled) or
127
+ * until the timeout expires / the abort signal fires.
128
+ *
129
+ * Strategy:
130
+ * 1. Try the SDK's `getTaskStatus` first (it shares websocket cache).
131
+ * 2. Fall back to direct HTTP if the SDK returns undefined.
132
+ *
133
+ * Errors:
134
+ * - RelayerTimeoutError on timeout / abort
135
+ * - RelayerExecutionFailedError on `failed`/`cancelled` terminal state
136
+ */
137
+ export async function pollGelatoTaskUntilTerminal(taskId, opts) {
138
+ const deadline = Date.now() + opts.pollTimeoutMs;
139
+ const r = relay();
140
+ while (Date.now() < deadline) {
141
+ if (opts.signal?.aborted === true) {
142
+ throw new RelayerTimeoutError();
143
+ }
144
+ let status;
145
+ try {
146
+ const upstream = await r.getTaskStatus(taskId);
147
+ if (upstream !== undefined) {
148
+ const built = {
149
+ taskId,
150
+ state: mapTaskState(upstream.taskState),
151
+ };
152
+ if (typeof upstream.transactionHash === 'string' &&
153
+ upstream.transactionHash.startsWith('0x')) {
154
+ built.txHash = upstream.transactionHash;
155
+ }
156
+ if (typeof upstream.blockNumber === 'number') {
157
+ built.blockNumber = BigInt(upstream.blockNumber);
158
+ }
159
+ if (typeof upstream.lastCheckMessage === 'string' &&
160
+ upstream.lastCheckMessage.length > 0) {
161
+ built.lastErrorMsg = upstream.lastCheckMessage;
162
+ }
163
+ status = built;
164
+ }
165
+ }
166
+ catch {
167
+ // swallow; we'll try the HTTP fallback or retry next tick
168
+ }
169
+ if (status === undefined) {
170
+ status = await fetchTaskStatusFallback(taskId, opts.signal);
171
+ }
172
+ if (status !== undefined) {
173
+ if (status.state === 'success')
174
+ return status;
175
+ if (status.state === 'failed' || status.state === 'cancelled') {
176
+ throw new RelayerExecutionFailedError(status.state);
177
+ }
178
+ // pending / submitted: keep polling
179
+ }
180
+ await sleep(opts.pollIntervalMs, opts.signal);
181
+ }
182
+ throw new RelayerTimeoutError();
183
+ }
184
+ async function sleep(ms, signal) {
185
+ return new Promise((resolve, reject) => {
186
+ if (signal?.aborted === true) {
187
+ reject(new RelayerTimeoutError());
188
+ return;
189
+ }
190
+ const t = setTimeout(() => {
191
+ if (signal !== undefined) {
192
+ signal.removeEventListener('abort', onAbort);
193
+ }
194
+ resolve();
195
+ }, ms);
196
+ const onAbort = () => {
197
+ clearTimeout(t);
198
+ reject(new RelayerTimeoutError());
199
+ };
200
+ if (signal !== undefined) {
201
+ signal.addEventListener('abort', onAbort, { once: true });
202
+ }
203
+ });
204
+ }
205
+ //# sourceMappingURL=gelato-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gelato-adapter.js","sourceRoot":"","sources":["../../src/gelato-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { relayedSweepERC20 } from './relayer.js';
2
+ export { relayedSweepETH } from './eth-sweep.js';
3
+ export { signEip2612Permit } from './signing.js';
4
+ export type { RelayerSweepOptions, RelayerSweepReceipt, RelayerSweepStatus, GelatoTaskId, Eip2612PermitSignature, RelayedEthSweepOptions, RelayedEthSweepReceipt, } from './types.js';
5
+ export { RelayerError, RelayerSignatureError, RelayerSubmissionError, RelayerExecutionFailedError, RelayerTimeoutError, RelayerForwarderMismatchError, StealthAddressEmptyError, RelayerInvalidChainError, } from './errors.js';
6
+ export { DEFAULT_PERMIT_DEADLINE_SECS, DEFAULT_POLL_INTERVAL_MS, DEFAULT_POLL_TIMEOUT_MS, GELATO_FORWARDER_BY_CHAIN, DEFAULT_ETH_SWEEP_DEADLINE_SECS, ETH_RELAYER_SUPPORTED_CHAINS, } from './constants.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,sBAAsB,EACtB,2BAA2B,EAC3B,mBAAmB,EACnB,6BAA6B,EAC7B,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { relayedSweepERC20 } from './relayer.js';
2
+ export { relayedSweepETH } from './eth-sweep.js';
3
+ export { signEip2612Permit } from './signing.js';
4
+ export { RelayerError, RelayerSignatureError, RelayerSubmissionError, RelayerExecutionFailedError, RelayerTimeoutError, RelayerForwarderMismatchError, StealthAddressEmptyError, RelayerInvalidChainError, } from './errors.js';
5
+ export { DEFAULT_PERMIT_DEADLINE_SECS, DEFAULT_POLL_INTERVAL_MS, DEFAULT_POLL_TIMEOUT_MS, GELATO_FORWARDER_BY_CHAIN, DEFAULT_ETH_SWEEP_DEADLINE_SECS, ETH_RELAYER_SUPPORTED_CHAINS, } from './constants.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAYjD,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,sBAAsB,EACtB,2BAA2B,EAC3B,mBAAmB,EACnB,6BAA6B,EAC7B,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { Address, Hex } from 'viem';
2
+ import type { ShroudFiTransport } from '@shroud-fi/transport';
3
+ import type { RelayerSweepOptions, RelayerSweepReceipt } from './types.js';
4
+ /**
5
+ * Gasless ERC-20 sweep via the deployed `ShroudFiRelayer` + Gelato.
6
+ *
7
+ * Flow:
8
+ * 1. Validate chain is in the supported Gelato forwarder table.
9
+ * 2. Read the relayer's on-chain trustedForwarder and assert match.
10
+ * 3. Read the stealth address ERC-20 balance (= permit `value`).
11
+ * 4. Sign an EIP-2612 permit { owner=stealth, spender=relayer, value, deadline }.
12
+ * 5. Encode `sweepERC20WithPermit(token, destination, deadline, v, r, s)`.
13
+ * 6. Submit via `sponsoredCallERC2771` (1Balance sponsored).
14
+ * 7. Poll until terminal — return receipt with txHash + blockNumber.
15
+ *
16
+ * Privacy invariants enforced here:
17
+ * - `stealthPrivateKey` is never logged, serialized, or attached to errors.
18
+ * - The receipt contains NO amount fields (balance is read but discarded
19
+ * after permit signing).
20
+ * - Error messages contain no addresses, amounts, signature bytes, or chain ids.
21
+ */
22
+ export declare function relayedSweepERC20(transport: ShroudFiTransport, stealthPrivateKey: Hex, token: Address, destination: Address, relayerContract: Address, options?: RelayerSweepOptions): Promise<RelayerSweepReceipt>;
23
+ //# sourceMappingURL=relayer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relayer.d.ts","sourceRoot":"","sources":["../../src/relayer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAGzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAO9D,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAuDpB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,iBAAiB,EAC5B,iBAAiB,EAAE,GAAG,EACtB,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,OAAO,EACpB,eAAe,EAAE,OAAO,EACxB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAqH9B"}
@@ -0,0 +1,146 @@
1
+ import { encodeFunctionData, getAddress } from 'viem';
2
+ import { privateKeyToAccount } from 'viem/accounts';
3
+ import { DEFAULT_PERMIT_DEADLINE_SECS, DEFAULT_POLL_INTERVAL_MS, DEFAULT_POLL_TIMEOUT_MS, GELATO_FORWARDER_BY_CHAIN, } from './constants.js';
4
+ import { RelayerForwarderMismatchError, StealthAddressEmptyError, RelayerInvalidChainError, } from './errors.js';
5
+ import { signEip2612Permit } from './signing.js';
6
+ import { submitSponsoredCallErc2771, pollGelatoTaskUntilTerminal, } from './gelato-adapter.js';
7
+ /**
8
+ * Minimal inline ABI for `ShroudFiRelayer` — only the surface we call from
9
+ * the SDK. Kept here (not in a shared `abi.ts`) because no other module needs
10
+ * these entries today.
11
+ */
12
+ const SHROUDFI_RELAYER_ABI = [
13
+ {
14
+ type: 'function',
15
+ name: 'sweepERC20WithPermit',
16
+ stateMutability: 'nonpayable',
17
+ inputs: [
18
+ { name: 'token', type: 'address' },
19
+ { name: 'destination', type: 'address' },
20
+ { name: 'deadline', type: 'uint256' },
21
+ { name: 'v', type: 'uint8' },
22
+ { name: 'r', type: 'bytes32' },
23
+ { name: 's', type: 'bytes32' },
24
+ ],
25
+ outputs: [],
26
+ },
27
+ {
28
+ type: 'function',
29
+ name: 'trustedForwarder',
30
+ stateMutability: 'view',
31
+ inputs: [],
32
+ outputs: [{ name: '', type: 'address' }],
33
+ },
34
+ ];
35
+ /**
36
+ * Minimal balanceOf ABI for the pre-flight balance read. Inlined so this file
37
+ * has no dependency on `@shroud-fi/transport`'s ERC20Abi shape.
38
+ */
39
+ const ERC20_BALANCE_OF_ABI = [
40
+ {
41
+ type: 'function',
42
+ name: 'balanceOf',
43
+ stateMutability: 'view',
44
+ inputs: [{ name: 'account', type: 'address' }],
45
+ outputs: [{ name: '', type: 'uint256' }],
46
+ },
47
+ ];
48
+ /**
49
+ * Gasless ERC-20 sweep via the deployed `ShroudFiRelayer` + Gelato.
50
+ *
51
+ * Flow:
52
+ * 1. Validate chain is in the supported Gelato forwarder table.
53
+ * 2. Read the relayer's on-chain trustedForwarder and assert match.
54
+ * 3. Read the stealth address ERC-20 balance (= permit `value`).
55
+ * 4. Sign an EIP-2612 permit { owner=stealth, spender=relayer, value, deadline }.
56
+ * 5. Encode `sweepERC20WithPermit(token, destination, deadline, v, r, s)`.
57
+ * 6. Submit via `sponsoredCallERC2771` (1Balance sponsored).
58
+ * 7. Poll until terminal — return receipt with txHash + blockNumber.
59
+ *
60
+ * Privacy invariants enforced here:
61
+ * - `stealthPrivateKey` is never logged, serialized, or attached to errors.
62
+ * - The receipt contains NO amount fields (balance is read but discarded
63
+ * after permit signing).
64
+ * - Error messages contain no addresses, amounts, signature bytes, or chain ids.
65
+ */
66
+ export async function relayedSweepERC20(transport, stealthPrivateKey, token, destination, relayerContract, options) {
67
+ const chainId = transport.chain.id;
68
+ // 1. Chain support check.
69
+ const expectedForwarder = GELATO_FORWARDER_BY_CHAIN[chainId];
70
+ if (expectedForwarder === undefined) {
71
+ throw new RelayerInvalidChainError();
72
+ }
73
+ // 2. Forwarder match check (defense-in-depth).
74
+ const onChainForwarder = (await transport.publicClient.readContract({
75
+ address: relayerContract,
76
+ abi: SHROUDFI_RELAYER_ABI,
77
+ functionName: 'trustedForwarder',
78
+ }));
79
+ if (getAddress(onChainForwarder) !== getAddress(expectedForwarder)) {
80
+ throw new RelayerForwarderMismatchError();
81
+ }
82
+ // 3. Balance read. Note: `account` is derived inside a tight scope; we
83
+ // never persist or log the privateKey-derived account beyond this.
84
+ const stealthAddress = privateKeyToAccount(stealthPrivateKey).address;
85
+ const balance = (await transport.publicClient.readContract({
86
+ address: token,
87
+ abi: ERC20_BALANCE_OF_ABI,
88
+ functionName: 'balanceOf',
89
+ args: [stealthAddress],
90
+ }));
91
+ if (balance === 0n) {
92
+ throw new StealthAddressEmptyError();
93
+ }
94
+ // 4. Compute permit deadline. Use block.timestamp + lifetime so the
95
+ // deadline aligns with the chain's clock rather than the host's.
96
+ const lifetime = options?.deadlineSecs ?? DEFAULT_PERMIT_DEADLINE_SECS;
97
+ const latestBlock = await transport.publicClient.getBlock();
98
+ const deadline = latestBlock.timestamp + lifetime;
99
+ // 5. Sign the EIP-2612 permit.
100
+ const permit = await signEip2612Permit(transport, stealthPrivateKey, token, relayerContract, balance, deadline);
101
+ // 6. Encode sweepERC20WithPermit calldata.
102
+ const calldata = encodeFunctionData({
103
+ abi: SHROUDFI_RELAYER_ABI,
104
+ functionName: 'sweepERC20WithPermit',
105
+ args: [token, destination, permit.deadline, permit.v, permit.r, permit.s],
106
+ });
107
+ // 7. Submit via Gelato 1Balance sponsored call.
108
+ const submitParams = {
109
+ chainId,
110
+ target: relayerContract,
111
+ data: calldata,
112
+ user: stealthAddress,
113
+ userPrivateKey: stealthPrivateKey,
114
+ chain: transport.chain,
115
+ };
116
+ if (options?.gelatoApiKey !== undefined) {
117
+ submitParams.apiKey = options.gelatoApiKey;
118
+ }
119
+ if (options?.signal !== undefined) {
120
+ submitParams.signal = options.signal;
121
+ }
122
+ const taskId = await submitSponsoredCallErc2771(submitParams);
123
+ // 8. Poll until terminal.
124
+ const pollOpts = {
125
+ pollIntervalMs: options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,
126
+ pollTimeoutMs: options?.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS,
127
+ };
128
+ if (options?.signal !== undefined) {
129
+ pollOpts.signal = options.signal;
130
+ }
131
+ const status = await pollGelatoTaskUntilTerminal(taskId, pollOpts);
132
+ // Build the receipt. NOTE: no amount fields by design.
133
+ const receipt = {
134
+ taskId,
135
+ txHash: status.txHash ?? '0x',
136
+ status: status.state,
137
+ relayerContract,
138
+ token,
139
+ destination,
140
+ };
141
+ if (status.blockNumber !== undefined) {
142
+ receipt.blockNumber = status.blockNumber;
143
+ }
144
+ return receipt;
145
+ }
146
+ //# sourceMappingURL=relayer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relayer.js","sourceRoot":"","sources":["../../src/relayer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AAKxB,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAE7B;;;;GAIG;AACH,MAAM,oBAAoB,GAAG;IAC3B;QACE,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,sBAAsB;QAC5B,eAAe,EAAE,YAAY;QAC7B,MAAM,EAAE;YACN,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;YAClC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE;YACxC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE;YACrC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE;YAC5B,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE;YAC9B,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE;SAC/B;QACD,OAAO,EAAE,EAAE;KACZ;IACD;QACE,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,kBAAkB;QACxB,eAAe,EAAE,MAAM;QACvB,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;KACzC;CACO,CAAC;AAEX;;;GAGG;AACH,MAAM,oBAAoB,GAAG;IAC3B;QACE,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,WAAW;QACjB,eAAe,EAAE,MAAM;QACvB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QAC9C,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;KACzC;CACO,CAAC;AAEX;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAA4B,EAC5B,iBAAsB,EACtB,KAAc,EACd,WAAoB,EACpB,eAAwB,EACxB,OAA6B;IAE7B,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;IAEnC,0BAA0B;IAC1B,MAAM,iBAAiB,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAC7D,IAAI,iBAAiB,KAAK,SAAS,EAAE,CAAC;QACpC,MAAM,IAAI,wBAAwB,EAAE,CAAC;IACvC,CAAC;IAED,+CAA+C;IAC/C,MAAM,gBAAgB,GAAG,CAAC,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC;QAClE,OAAO,EAAE,eAAe;QACxB,GAAG,EAAE,oBAAoB;QACzB,YAAY,EAAE,kBAAkB;KACjC,CAAC,CAAY,CAAC;IACf,IACE,UAAU,CAAC,gBAAgB,CAAC,KAAK,UAAU,CAAC,iBAAiB,CAAC,EAC9D,CAAC;QACD,MAAM,IAAI,6BAA6B,EAAE,CAAC;IAC5C,CAAC;IAED,uEAAuE;IACvE,mEAAmE;IACnE,MAAM,cAAc,GAAG,mBAAmB,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;IAEtE,MAAM,OAAO,GAAG,CAAC,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC;QACzD,OAAO,EAAE,KAAK;QACd,GAAG,EAAE,oBAAoB;QACzB,YAAY,EAAE,WAAW;QACzB,IAAI,EAAE,CAAC,cAAc,CAAC;KACvB,CAAC,CAAW,CAAC;IAEd,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACnB,MAAM,IAAI,wBAAwB,EAAE,CAAC;IACvC,CAAC;IAED,oEAAoE;IACpE,iEAAiE;IACjE,MAAM,QAAQ,GAAG,OAAO,EAAE,YAAY,IAAI,4BAA4B,CAAC;IACvE,MAAM,WAAW,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC;IAC5D,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,GAAG,QAAQ,CAAC;IAElD,+BAA+B;IAC/B,MAAM,MAAM,GAAG,MAAM,iBAAiB,CACpC,SAAS,EACT,iBAAiB,EACjB,KAAK,EACL,eAAe,EACf,OAAO,EACP,QAAQ,CACT,CAAC;IAEF,2CAA2C;IAC3C,MAAM,QAAQ,GAAG,kBAAkB,CAAC;QAClC,GAAG,EAAE,oBAAoB;QACzB,YAAY,EAAE,sBAAsB;QACpC,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;KAC1E,CAAC,CAAC;IAEH,gDAAgD;IAChD,MAAM,YAAY,GAUd;QACF,OAAO;QACP,MAAM,EAAE,eAAe;QACvB,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,cAAc;QACpB,cAAc,EAAE,iBAAiB;QACjC,KAAK,EAAE,SAAS,CAAC,KAAK;KACvB,CAAC;IACF,IAAI,OAAO,EAAE,YAAY,KAAK,SAAS,EAAE,CAAC;QACxC,YAAY,CAAC,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAC7C,CAAC;IACD,IAAI,OAAO,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,YAAY,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IACvC,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,YAAY,CAAC,CAAC;IAE9D,0BAA0B;IAC1B,MAAM,QAAQ,GAIV;QACF,cAAc,EAAE,OAAO,EAAE,cAAc,IAAI,wBAAwB;QACnE,aAAa,EAAE,OAAO,EAAE,aAAa,IAAI,uBAAuB;KACjE,CAAC;IACF,IAAI,OAAO,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,QAAQ,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IACnC,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,2BAA2B,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEnE,uDAAuD;IACvD,MAAM,OAAO,GAET;QACF,MAAM;QACN,MAAM,EAAE,MAAM,CAAC,MAAM,IAAK,IAAY;QACtC,MAAM,EAAE,MAAM,CAAC,KAAK;QACpB,eAAe;QACf,KAAK;QACL,WAAW;KACZ,CAAC;IACF,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACrC,OAAO,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;IAC3C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { Address, Hex } from 'viem';
2
+ import type { ShroudFiTransport } from '@shroud-fi/transport';
3
+ import type { Eip2612PermitSignature } from './types.js';
4
+ /**
5
+ * Sign an EIP-2612 permit for {token, spender, value, deadline} from the
6
+ * stealth address keyed by `stealthPrivateKey`.
7
+ *
8
+ * Privacy:
9
+ * - The privateKey is consumed once to derive a viem `account` and is not
10
+ * stored, logged, serialized, or attached to thrown errors.
11
+ * - On failure we throw a structured `RelayerSignatureError` with no key
12
+ * material, no signature bytes, no nonce.
13
+ * - Token `version()` is probed with a try/catch fallback to "1" — the
14
+ * dominant default across permit-compatible ERC-20s.
15
+ *
16
+ * @param transport - ShroudFi transport (publicClient for reads)
17
+ * @param stealthPrivateKey - 0x-prefixed 32-byte private key (treated as secret)
18
+ * @param token - ERC-20 contract
19
+ * @param spender - relayer contract address (= `ShroudFiRelayer`)
20
+ * @param value - permit value (= full balance at time of sign)
21
+ * @param deadline - unix-seconds deadline (caller computes from `block.timestamp + lifetime`)
22
+ */
23
+ export declare function signEip2612Permit(transport: ShroudFiTransport, stealthPrivateKey: Hex, token: Address, spender: Address, value: bigint, deadline: bigint): Promise<Eip2612PermitSignature>;
24
+ //# sourceMappingURL=signing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signing.d.ts","sourceRoot":"","sources":["../../src/signing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAGzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AA2CzD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,iBAAiB,EAC5B,iBAAiB,EAAE,GAAG,EACtB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,sBAAsB,CAAC,CA2FjC"}
@@ -0,0 +1,152 @@
1
+ import { parseSignature } from 'viem';
2
+ import { privateKeyToAccount } from 'viem/accounts';
3
+ import { RelayerSignatureError } from './errors.js';
4
+ /**
5
+ * Minimal ABI for the read paths we use. Inlined rather than imported from
6
+ * `@shroud-fi/transport`'s ERC20Abi so this file has zero coupling to
7
+ * non-permit fields. Direct two-call reads (no multicall3 dependency) so the
8
+ * SDK runs against forks/local Anvils where multicall may not be configured.
9
+ */
10
+ const PERMIT_READ_ABI = [
11
+ {
12
+ type: 'function',
13
+ name: 'name',
14
+ stateMutability: 'view',
15
+ inputs: [],
16
+ outputs: [{ name: '', type: 'string' }],
17
+ },
18
+ {
19
+ type: 'function',
20
+ name: 'version',
21
+ stateMutability: 'view',
22
+ inputs: [],
23
+ outputs: [{ name: '', type: 'string' }],
24
+ },
25
+ {
26
+ type: 'function',
27
+ name: 'nonces',
28
+ stateMutability: 'view',
29
+ inputs: [{ name: 'owner', type: 'address' }],
30
+ outputs: [{ name: '', type: 'uint256' }],
31
+ },
32
+ ];
33
+ const PERMIT_TYPES = {
34
+ Permit: [
35
+ { name: 'owner', type: 'address' },
36
+ { name: 'spender', type: 'address' },
37
+ { name: 'value', type: 'uint256' },
38
+ { name: 'nonce', type: 'uint256' },
39
+ { name: 'deadline', type: 'uint256' },
40
+ ],
41
+ };
42
+ /**
43
+ * Sign an EIP-2612 permit for {token, spender, value, deadline} from the
44
+ * stealth address keyed by `stealthPrivateKey`.
45
+ *
46
+ * Privacy:
47
+ * - The privateKey is consumed once to derive a viem `account` and is not
48
+ * stored, logged, serialized, or attached to thrown errors.
49
+ * - On failure we throw a structured `RelayerSignatureError` with no key
50
+ * material, no signature bytes, no nonce.
51
+ * - Token `version()` is probed with a try/catch fallback to "1" — the
52
+ * dominant default across permit-compatible ERC-20s.
53
+ *
54
+ * @param transport - ShroudFi transport (publicClient for reads)
55
+ * @param stealthPrivateKey - 0x-prefixed 32-byte private key (treated as secret)
56
+ * @param token - ERC-20 contract
57
+ * @param spender - relayer contract address (= `ShroudFiRelayer`)
58
+ * @param value - permit value (= full balance at time of sign)
59
+ * @param deadline - unix-seconds deadline (caller computes from `block.timestamp + lifetime`)
60
+ */
61
+ export async function signEip2612Permit(transport, stealthPrivateKey, token, spender, value, deadline) {
62
+ const account = privateKeyToAccount(stealthPrivateKey);
63
+ const owner = account.address;
64
+ const chainId = transport.chain.id;
65
+ // Read token name (required for the domain separator). If a token doesn't
66
+ // expose `name()` it isn't ERC-2612 compatible — propagate as signature error.
67
+ let tokenName;
68
+ try {
69
+ tokenName = (await transport.publicClient.readContract({
70
+ address: token,
71
+ abi: PERMIT_READ_ABI,
72
+ functionName: 'name',
73
+ }));
74
+ }
75
+ catch {
76
+ throw new RelayerSignatureError();
77
+ }
78
+ // Token version: most permit ERC-20s use "1". Probe with a try/catch.
79
+ let tokenVersion = '1';
80
+ try {
81
+ const v = (await transport.publicClient.readContract({
82
+ address: token,
83
+ abi: PERMIT_READ_ABI,
84
+ functionName: 'version',
85
+ }));
86
+ if (typeof v === 'string' && v.length > 0) {
87
+ tokenVersion = v;
88
+ }
89
+ }
90
+ catch {
91
+ // intentional: fall back to "1"
92
+ }
93
+ // Read the current permit nonce for `owner`.
94
+ let nonce;
95
+ try {
96
+ nonce = (await transport.publicClient.readContract({
97
+ address: token,
98
+ abi: PERMIT_READ_ABI,
99
+ functionName: 'nonces',
100
+ args: [owner],
101
+ }));
102
+ }
103
+ catch {
104
+ throw new RelayerSignatureError();
105
+ }
106
+ const domain = {
107
+ name: tokenName,
108
+ version: tokenVersion,
109
+ chainId,
110
+ verifyingContract: token,
111
+ };
112
+ let signatureHex;
113
+ try {
114
+ signatureHex = await account.signTypedData({
115
+ domain,
116
+ types: PERMIT_TYPES,
117
+ primaryType: 'Permit',
118
+ message: {
119
+ owner,
120
+ spender,
121
+ value,
122
+ nonce,
123
+ deadline,
124
+ },
125
+ });
126
+ }
127
+ catch {
128
+ throw new RelayerSignatureError();
129
+ }
130
+ // viem returns the 65-byte signature; split into v/r/s.
131
+ // `parseSignature` returns either {r,s,v,yParity} or {r,s,yParity} (v?: never).
132
+ // Standard EIP-2612 needs `v ∈ {27, 28}`: derive from yParity if v is absent.
133
+ let v;
134
+ let r;
135
+ let s;
136
+ try {
137
+ const split = parseSignature(signatureHex);
138
+ r = split.r;
139
+ s = split.s;
140
+ if (split.v !== undefined) {
141
+ v = Number(split.v);
142
+ }
143
+ else {
144
+ v = split.yParity === 0 ? 27 : 28;
145
+ }
146
+ }
147
+ catch {
148
+ throw new RelayerSignatureError();
149
+ }
150
+ return { v, r, s, deadline };
151
+ }
152
+ //# sourceMappingURL=signing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signing.js","sourceRoot":"","sources":["../../src/signing.ts"],"names":[],"mappings":"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"}