@shroud-fi/self-host-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 +64 -0
  3. package/dist/cjs/dev-entry.d.ts +24 -0
  4. package/dist/cjs/dev-entry.d.ts.map +1 -0
  5. package/dist/cjs/dev-entry.js +75 -0
  6. package/dist/cjs/dev-entry.js.map +1 -0
  7. package/dist/cjs/errors.d.ts +41 -0
  8. package/dist/cjs/errors.d.ts.map +1 -0
  9. package/dist/cjs/errors.js +63 -0
  10. package/dist/cjs/errors.js.map +1 -0
  11. package/dist/cjs/index.d.ts +8 -0
  12. package/dist/cjs/index.d.ts.map +1 -0
  13. package/dist/cjs/index.js +26 -0
  14. package/dist/cjs/index.js.map +1 -0
  15. package/dist/cjs/package.json +1 -0
  16. package/dist/cjs/prod-entry.d.ts +33 -0
  17. package/dist/cjs/prod-entry.d.ts.map +1 -0
  18. package/dist/cjs/prod-entry.js +98 -0
  19. package/dist/cjs/prod-entry.js.map +1 -0
  20. package/dist/cjs/relay-eth.d.ts +43 -0
  21. package/dist/cjs/relay-eth.d.ts.map +1 -0
  22. package/dist/cjs/relay-eth.js +95 -0
  23. package/dist/cjs/relay-eth.js.map +1 -0
  24. package/dist/cjs/relay.d.ts +38 -0
  25. package/dist/cjs/relay.d.ts.map +1 -0
  26. package/dist/cjs/relay.js +83 -0
  27. package/dist/cjs/relay.js.map +1 -0
  28. package/dist/cjs/server.d.ts +18 -0
  29. package/dist/cjs/server.d.ts.map +1 -0
  30. package/dist/cjs/server.js +384 -0
  31. package/dist/cjs/server.js.map +1 -0
  32. package/dist/cjs/types.d.ts +83 -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/dev-entry.d.ts +24 -0
  37. package/dist/esm/dev-entry.d.ts.map +1 -0
  38. package/dist/esm/dev-entry.js +73 -0
  39. package/dist/esm/dev-entry.js.map +1 -0
  40. package/dist/esm/errors.d.ts +41 -0
  41. package/dist/esm/errors.d.ts.map +1 -0
  42. package/dist/esm/errors.js +53 -0
  43. package/dist/esm/errors.js.map +1 -0
  44. package/dist/esm/index.d.ts +8 -0
  45. package/dist/esm/index.d.ts.map +1 -0
  46. package/dist/esm/index.js +10 -0
  47. package/dist/esm/index.js.map +1 -0
  48. package/dist/esm/prod-entry.d.ts +33 -0
  49. package/dist/esm/prod-entry.d.ts.map +1 -0
  50. package/dist/esm/prod-entry.js +96 -0
  51. package/dist/esm/prod-entry.js.map +1 -0
  52. package/dist/esm/relay-eth.d.ts +43 -0
  53. package/dist/esm/relay-eth.d.ts.map +1 -0
  54. package/dist/esm/relay-eth.js +91 -0
  55. package/dist/esm/relay-eth.js.map +1 -0
  56. package/dist/esm/relay.d.ts +38 -0
  57. package/dist/esm/relay.d.ts.map +1 -0
  58. package/dist/esm/relay.js +79 -0
  59. package/dist/esm/relay.js.map +1 -0
  60. package/dist/esm/server.d.ts +18 -0
  61. package/dist/esm/server.d.ts.map +1 -0
  62. package/dist/esm/server.js +377 -0
  63. package/dist/esm/server.js.map +1 -0
  64. package/dist/esm/types.d.ts +83 -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 +65 -0
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Core relay logic. Pure function — given a validated request + viem clients,
3
+ * encodes the ERC-2771 wrapped calldata and broadcasts via the relayer EOA.
4
+ *
5
+ * Privacy invariants:
6
+ * - The relayer EOA's private key is held in a viem account closure. It is
7
+ * never logged, serialized, or attached to thrown errors.
8
+ * - The stealth address is data, not key material.
9
+ * - The permit (v, r, s, deadline) is a one-time authorization for the
10
+ * specific (owner, spender, value, deadline) bound at signing time.
11
+ */
12
+ import { encodeFunctionData, } from 'viem';
13
+ import { RelayBroadcastError } from './errors.js';
14
+ const SWEEP_ABI = [
15
+ {
16
+ type: 'function',
17
+ name: 'sweepERC20WithPermit',
18
+ stateMutability: 'nonpayable',
19
+ inputs: [
20
+ { name: 'token', type: 'address' },
21
+ { name: 'destination', type: 'address' },
22
+ { name: 'deadline', type: 'uint256' },
23
+ { name: 'v', type: 'uint8' },
24
+ { name: 'r', type: 'bytes32' },
25
+ { name: 's', type: 'bytes32' },
26
+ ],
27
+ outputs: [],
28
+ },
29
+ ];
30
+ /**
31
+ * Build the ERC-2771-wrapped calldata: encoded sweepERC20WithPermit(...) with
32
+ * the 20-byte stealth address appended. ShroudFiRelayer's ERC2771Context
33
+ * reads _msgSender() as the appended address when msg.sender is the trusted
34
+ * forwarder (= the relayer EOA = `account` here).
35
+ */
36
+ export function buildRelayCalldata(req) {
37
+ const inner = encodeFunctionData({
38
+ abi: SWEEP_ABI,
39
+ functionName: 'sweepERC20WithPermit',
40
+ args: [
41
+ req.token,
42
+ req.destination,
43
+ BigInt(req.deadline),
44
+ req.v,
45
+ req.r,
46
+ req.s,
47
+ ],
48
+ });
49
+ const suffix = req.stealthAddress.slice(2).toLowerCase();
50
+ // Inner is `0x...`; concatenate stealth bytes (20 = 40 hex chars).
51
+ return (inner + suffix);
52
+ }
53
+ /**
54
+ * Broadcast the wrapped tx. Waits for the receipt before returning.
55
+ *
56
+ * Throws RelayBroadcastError on any underlying viem error. Errors are wrapped
57
+ * to strip out potentially key-bearing payloads from the visible message.
58
+ */
59
+ export async function relayRequest(ctx, req) {
60
+ const data = buildRelayCalldata(req);
61
+ try {
62
+ const txHash = await ctx.walletClient.sendTransaction({
63
+ account: ctx.account,
64
+ chain: ctx.walletClient.chain ?? null,
65
+ to: ctx.relayerContract,
66
+ data,
67
+ value: 0n,
68
+ });
69
+ const receipt = await ctx.publicClient.waitForTransactionReceipt({
70
+ hash: txHash,
71
+ });
72
+ return { txHash, blockNumber: receipt.blockNumber };
73
+ }
74
+ catch (err) {
75
+ const name = err instanceof Error ? err.name : 'UnknownError';
76
+ throw new RelayBroadcastError(name, { cause: err });
77
+ }
78
+ }
79
+ //# sourceMappingURL=relay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay.js","sourceRoot":"","sources":["../../src/relay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,kBAAkB,GAQnB,MAAM,MAAM,CAAC;AACd,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlD,MAAM,SAAS,GAAG;IAChB;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;CACO,CAAC;AAkBX;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAiB;IAClD,MAAM,KAAK,GAAG,kBAAkB,CAAC;QAC/B,GAAG,EAAE,SAAS;QACd,YAAY,EAAE,sBAAsB;QACpC,IAAI,EAAE;YACJ,GAAG,CAAC,KAAK;YACT,GAAG,CAAC,WAAW;YACf,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACpB,GAAG,CAAC,CAAC;YACL,GAAG,CAAC,CAAC;YACL,GAAG,CAAC,CAAC;SACN;KACF,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACzD,mEAAmE;IACnE,OAAO,CAAC,KAAK,GAAG,MAAM,CAAQ,CAAC;AACjC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAa,EACb,GAAiB;IAEjB,MAAM,IAAI,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,eAAe,CAAC;YACpD,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,KAAK,EAAE,GAAG,CAAC,YAAY,CAAC,KAAK,IAAI,IAAI;YACrC,EAAE,EAAE,GAAG,CAAC,eAAe;YACvB,IAAI;YACJ,KAAK,EAAE,EAAE;SACV,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,yBAAyB,CAAC;YAC/D,IAAI,EAAE,MAAM;SACb,CAAC,CAAC;QACH,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;IACtD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC;QAC9D,MAAM,IAAI,mBAAmB,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Fastify server for @shroud-fi/self-host-relayer.
3
+ *
4
+ * Endpoints:
5
+ * POST /relay - submit a permit-signed sweep request
6
+ * GET /health - liveness probe
7
+ *
8
+ * Privacy invariants:
9
+ * - Request body is validated then discarded — never logged.
10
+ * - Error responses carry only error class names, never inner messages.
11
+ * - The forwarder EOA's private key is read from config once, then held in
12
+ * a viem account closure. Never logged, never serialized to disk.
13
+ */
14
+ import { type FastifyInstance } from 'fastify';
15
+ import type { ServiceConfig } from './types.js';
16
+ export declare function buildServer(cfg: ServiceConfig): Promise<FastifyInstance>;
17
+ export declare function startServer(cfg: ServiceConfig): Promise<FastifyInstance>;
18
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,SAAS,CAAC;AA6BxD,OAAO,KAAK,EAIV,aAAa,EACd,MAAM,YAAY,CAAC;AAgFpB,wBAAsB,WAAW,CAC/B,GAAG,EAAE,aAAa,GACjB,OAAO,CAAC,eAAe,CAAC,CA+Q1B;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC,CAI9E"}
@@ -0,0 +1,377 @@
1
+ /**
2
+ * Fastify server for @shroud-fi/self-host-relayer.
3
+ *
4
+ * Endpoints:
5
+ * POST /relay - submit a permit-signed sweep request
6
+ * GET /health - liveness probe
7
+ *
8
+ * Privacy invariants:
9
+ * - Request body is validated then discarded — never logged.
10
+ * - Error responses carry only error class names, never inner messages.
11
+ * - The forwarder EOA's private key is read from config once, then held in
12
+ * a viem account closure. Never logged, never serialized to disk.
13
+ */
14
+ import Fastify from 'fastify';
15
+ import cors from '@fastify/cors';
16
+ import rateLimit from '@fastify/rate-limit';
17
+ import { createPublicClient, createWalletClient, http, isAddress, recoverTypedDataAddress, serializeSignature, getAddress, } from 'viem';
18
+ import { base, baseSepolia } from 'viem/chains';
19
+ import { privateKeyToAccount } from 'viem/accounts';
20
+ import { AuthorizationContractMismatchError, EthRelayerNotConfiguredError, InvalidRelayEthRequestError, InvalidRelayRequestError, } from './errors.js';
21
+ import { relayRequest } from './relay.js';
22
+ import { relayEthRequest } from './relay-eth.js';
23
+ const HEX32_RE = /^0x[0-9a-fA-F]{64}$/;
24
+ function isHex32(s) {
25
+ return typeof s === 'string' && HEX32_RE.test(s);
26
+ }
27
+ function validateRequest(body) {
28
+ if (typeof body !== 'object' || body === null) {
29
+ throw new InvalidRelayRequestError();
30
+ }
31
+ const b = body;
32
+ if (!isAddress(b.token))
33
+ throw new InvalidRelayRequestError();
34
+ if (!isAddress(b.destination))
35
+ throw new InvalidRelayRequestError();
36
+ if (!isAddress(b.stealthAddress))
37
+ throw new InvalidRelayRequestError();
38
+ if (typeof b.deadline !== 'string' || !/^\d+$/.test(b.deadline)) {
39
+ throw new InvalidRelayRequestError();
40
+ }
41
+ if (typeof b.v !== 'number' || b.v < 0 || b.v > 255) {
42
+ throw new InvalidRelayRequestError();
43
+ }
44
+ if (!isHex32(b.r))
45
+ throw new InvalidRelayRequestError();
46
+ if (!isHex32(b.s))
47
+ throw new InvalidRelayRequestError();
48
+ return {
49
+ token: b.token,
50
+ destination: b.destination,
51
+ stealthAddress: b.stealthAddress,
52
+ deadline: b.deadline,
53
+ v: b.v,
54
+ r: b.r,
55
+ s: b.s,
56
+ };
57
+ }
58
+ const HEX_PREFIXED_RE = /^0x[0-9a-fA-F]+$/;
59
+ function isHex(s) {
60
+ return typeof s === 'string' && HEX_PREFIXED_RE.test(s);
61
+ }
62
+ function validateEthRequest(body) {
63
+ if (typeof body !== 'object' || body === null) {
64
+ throw new InvalidRelayEthRequestError();
65
+ }
66
+ const b = body;
67
+ if (!isAddress(b.stealthAddress))
68
+ throw new InvalidRelayEthRequestError();
69
+ if (!isAddress(b.destination))
70
+ throw new InvalidRelayEthRequestError();
71
+ if (typeof b.chainId !== 'number')
72
+ throw new InvalidRelayEthRequestError();
73
+ if (typeof b.deadline !== 'string' || !/^\d+$/.test(b.deadline)) {
74
+ throw new InvalidRelayEthRequestError();
75
+ }
76
+ if (!isHex(b.signature))
77
+ throw new InvalidRelayEthRequestError();
78
+ const auth = b.authorization;
79
+ if (typeof auth !== 'object' || auth === null)
80
+ throw new InvalidRelayEthRequestError();
81
+ if (!isAddress(auth.address))
82
+ throw new InvalidRelayEthRequestError();
83
+ if (typeof auth.chainId !== 'number')
84
+ throw new InvalidRelayEthRequestError();
85
+ if (typeof auth.nonce !== 'number' || auth.nonce < 0) {
86
+ throw new InvalidRelayEthRequestError();
87
+ }
88
+ if (!isHex32(auth.r))
89
+ throw new InvalidRelayEthRequestError();
90
+ if (!isHex32(auth.s))
91
+ throw new InvalidRelayEthRequestError();
92
+ if (auth.yParity !== 0 && auth.yParity !== 1)
93
+ throw new InvalidRelayEthRequestError();
94
+ return {
95
+ stealthAddress: b.stealthAddress,
96
+ destination: b.destination,
97
+ chainId: b.chainId,
98
+ deadline: b.deadline,
99
+ signature: b.signature,
100
+ authorization: {
101
+ address: auth.address,
102
+ chainId: auth.chainId,
103
+ nonce: auth.nonce,
104
+ r: auth.r,
105
+ s: auth.s,
106
+ yParity: auth.yParity,
107
+ },
108
+ };
109
+ }
110
+ export async function buildServer(cfg) {
111
+ const fastify = Fastify({
112
+ // Logging stripped of body — only method, url, status, response time.
113
+ logger: { level: 'info' },
114
+ disableRequestLogging: true,
115
+ });
116
+ await fastify.register(cors, {
117
+ origin: (origin, cb) => {
118
+ // Allow same-host requests (no Origin header) for curl/healthchecks.
119
+ if (origin === undefined)
120
+ return cb(null, true);
121
+ if (cfg.allowedOrigins.includes(origin))
122
+ return cb(null, true);
123
+ return cb(new Error('Origin not allowed'), false);
124
+ },
125
+ });
126
+ await fastify.register(rateLimit, {
127
+ max: cfg.rateLimitPerMinute,
128
+ timeWindow: '1 minute',
129
+ });
130
+ const chain = cfg.chainId === 8453 ? base : baseSepolia;
131
+ const account = privateKeyToAccount(cfg.forwarderPrivateKey);
132
+ // Viem's OP-stack chain extension widens the tx type set. Our relayRequest
133
+ // signature uses a generic Chain; cast at the boundary so the OP extensions
134
+ // don't bleed into our public types.
135
+ const publicClient = createPublicClient({
136
+ chain,
137
+ transport: http(cfg.rpcUrl, { batch: false }),
138
+ });
139
+ const walletClient = createWalletClient({
140
+ chain,
141
+ account,
142
+ transport: http(cfg.rpcUrl, { batch: false }),
143
+ });
144
+ fastify.get('/health', async () => ({ ok: true, chainId: cfg.chainId }));
145
+ fastify.post('/relay', async (req, reply) => {
146
+ let validated;
147
+ try {
148
+ validated = validateRequest(req.body);
149
+ }
150
+ catch (err) {
151
+ const name = err instanceof Error ? err.name : 'InvalidRelayRequestError';
152
+ reply.status(400);
153
+ const body = { ok: false, error: name };
154
+ return body;
155
+ }
156
+ // Diagnostic pre-flight: read on-chain state for the (stealth, token) pair
157
+ // before broadcasting. NOT key material — all four values (balance,
158
+ // allowance, nonce, name) are public reads. Off in prod by default; flip
159
+ // cfg.debugDiagnostics = true (DEBUG_DIAGNOSTICS=1 env) to enable when
160
+ // troubleshooting permit digest mismatches.
161
+ if (cfg.debugDiagnostics === true)
162
+ try {
163
+ const tokenAbi = [
164
+ { type: 'function', name: 'balanceOf', stateMutability: 'view', inputs: [{ name: 'a', type: 'address' }], outputs: [{ type: 'uint256' }] },
165
+ { type: 'function', name: 'allowance', stateMutability: 'view', inputs: [{ name: 'o', type: 'address' }, { name: 's', type: 'address' }], outputs: [{ type: 'uint256' }] },
166
+ { type: 'function', name: 'nonces', stateMutability: 'view', inputs: [{ name: 'a', type: 'address' }], outputs: [{ type: 'uint256' }] },
167
+ { type: 'function', name: 'name', stateMutability: 'view', inputs: [], outputs: [{ type: 'string' }] },
168
+ { type: 'function', name: 'version', stateMutability: 'view', inputs: [], outputs: [{ type: 'string' }] },
169
+ ];
170
+ const [bal, allow, nonce, name] = await Promise.all([
171
+ publicClient.readContract({ address: validated.token, abi: tokenAbi, functionName: 'balanceOf', args: [validated.stealthAddress] }),
172
+ publicClient.readContract({ address: validated.token, abi: tokenAbi, functionName: 'allowance', args: [validated.stealthAddress, cfg.relayerContract] }),
173
+ publicClient.readContract({ address: validated.token, abi: tokenAbi, functionName: 'nonces', args: [validated.stealthAddress] }),
174
+ publicClient.readContract({ address: validated.token, abi: tokenAbi, functionName: 'name' }),
175
+ ]);
176
+ let probedVersion = 'unprobed';
177
+ try {
178
+ probedVersion = (await publicClient.readContract({
179
+ address: validated.token,
180
+ abi: tokenAbi,
181
+ functionName: 'version',
182
+ }));
183
+ }
184
+ catch {
185
+ probedVersion = 'NOT_EXPOSED(fallback_to_1)';
186
+ }
187
+ const now = Math.floor(Date.now() / 1000);
188
+ fastify.log.info({
189
+ stealth: validated.stealthAddress,
190
+ token: validated.token,
191
+ balance: bal.toString(),
192
+ allowance: allow.toString(),
193
+ nonce: nonce.toString(),
194
+ tokenName: name,
195
+ probedVersion,
196
+ deadline: validated.deadline,
197
+ nowUnix: now,
198
+ deadlineDelta: Number(BigInt(validated.deadline) - BigInt(now)),
199
+ v: validated.v,
200
+ }, 'relay preflight state');
201
+ // Signature recovery: rebuild the same EIP-712 message the agent SHOULD
202
+ // have signed (under both "1" and probed version) and see which (if any)
203
+ // recovers to the stealth address. Whichever ONE matches tells us the
204
+ // exact domain used. If NEITHER matches, the failure is in value/nonce/
205
+ // chainId/spender — list those candidates too.
206
+ try {
207
+ const fullSig = serializeSignature({ r: validated.r, s: validated.s, v: BigInt(validated.v) });
208
+ const message = {
209
+ owner: validated.stealthAddress,
210
+ spender: cfg.relayerContract,
211
+ value: bal,
212
+ nonce,
213
+ deadline: BigInt(validated.deadline),
214
+ };
215
+ const types = {
216
+ Permit: [
217
+ { name: 'owner', type: 'address' },
218
+ { name: 'spender', type: 'address' },
219
+ { name: 'value', type: 'uint256' },
220
+ { name: 'nonce', type: 'uint256' },
221
+ { name: 'deadline', type: 'uint256' },
222
+ ],
223
+ };
224
+ const candidates = [
225
+ { label: 'name=' + name + ',ver=1,chain=' + cfg.chainId,
226
+ domain: { name, version: '1', chainId: cfg.chainId, verifyingContract: validated.token } },
227
+ ];
228
+ if (probedVersion !== 'unprobed' && probedVersion !== 'NOT_EXPOSED(fallback_to_1)') {
229
+ candidates.push({
230
+ label: 'name=' + name + ',ver=' + probedVersion + ',chain=' + cfg.chainId,
231
+ domain: { name, version: probedVersion, chainId: cfg.chainId, verifyingContract: validated.token },
232
+ });
233
+ }
234
+ const recoveries = [];
235
+ for (const c of candidates) {
236
+ const recovered = await recoverTypedDataAddress({
237
+ domain: c.domain,
238
+ types,
239
+ primaryType: 'Permit',
240
+ message,
241
+ signature: fullSig,
242
+ });
243
+ recoveries.push({
244
+ label: c.label,
245
+ recovered,
246
+ matches: recovered.toLowerCase() === validated.stealthAddress.toLowerCase(),
247
+ });
248
+ }
249
+ fastify.log.info({ recoveries, expectedStealth: validated.stealthAddress }, 'sig recovery probe');
250
+ }
251
+ catch (sigErr) {
252
+ fastify.log.warn({ name: sigErr instanceof Error ? sigErr.name : 'unknown', msg: sigErr instanceof Error ? sigErr.message.slice(0, 200) : '' }, 'sig recovery threw');
253
+ }
254
+ }
255
+ catch (probeErr) {
256
+ fastify.log.warn({ name: probeErr instanceof Error ? probeErr.name : 'unknown' }, 'preflight probe failed');
257
+ }
258
+ try {
259
+ const result = await relayRequest({
260
+ publicClient,
261
+ walletClient,
262
+ account,
263
+ relayerContract: cfg.relayerContract,
264
+ }, validated);
265
+ const body = {
266
+ ok: true,
267
+ txHash: result.txHash,
268
+ blockNumber: result.blockNumber.toString(),
269
+ };
270
+ return body;
271
+ }
272
+ catch (err) {
273
+ const name = err instanceof Error ? err.name : 'RelayBroadcastError';
274
+ // Diagnostic: walk the cause chain and emit each viem error class +
275
+ // shortMessage (revert reason). These are public on-chain values, never
276
+ // key material. Logged via fastify.log.error so the operator can find
277
+ // the actual revert without surfacing it to clients.
278
+ const chain = [];
279
+ let cursor = err;
280
+ let depth = 0;
281
+ while (cursor instanceof Error && depth < 6) {
282
+ const sm = cursor.shortMessage;
283
+ const frame = { name: cursor.name };
284
+ if (sm !== undefined)
285
+ frame.shortMessage = sm;
286
+ chain.push(frame);
287
+ cursor = cursor.cause;
288
+ depth++;
289
+ }
290
+ fastify.log.error({ chain }, 'relay broadcast failed');
291
+ reply.status(502);
292
+ const body = { ok: false, error: name };
293
+ return body;
294
+ }
295
+ });
296
+ // ─── /relay-eth — P5.1 EIP-7702 ETH gasless sweep ─────────────────────
297
+ fastify.post('/relay-eth', async (req, reply) => {
298
+ // Refuse early if the operator hasn't pinned the deployed ETH relayer.
299
+ if (cfg.ethRelayerContract === undefined) {
300
+ reply.status(501);
301
+ const body = {
302
+ ok: false,
303
+ error: new EthRelayerNotConfiguredError().name,
304
+ };
305
+ return body;
306
+ }
307
+ let validated;
308
+ try {
309
+ validated = validateEthRequest(req.body);
310
+ }
311
+ catch (err) {
312
+ const name = err instanceof Error ? err.name : 'InvalidRelayEthRequestError';
313
+ reply.status(400);
314
+ const body = { ok: false, error: name };
315
+ return body;
316
+ }
317
+ // Defence in depth: the authorization MUST delegate to the configured
318
+ // ETH relayer contract. A mismatch means either (a) the SDK is talking
319
+ // to a different deployment, or (b) someone is trying to delegate to an
320
+ // attacker contract via this relayer. Either way, refuse.
321
+ if (getAddress(validated.authorization.address) !==
322
+ getAddress(cfg.ethRelayerContract)) {
323
+ reply.status(400);
324
+ const body = {
325
+ ok: false,
326
+ error: new AuthorizationContractMismatchError().name,
327
+ };
328
+ return body;
329
+ }
330
+ // Chain ID consistency: the SDK's chainId must match this service's chain.
331
+ if (validated.chainId !== cfg.chainId) {
332
+ reply.status(400);
333
+ const body = {
334
+ ok: false,
335
+ error: new InvalidRelayEthRequestError().name,
336
+ };
337
+ return body;
338
+ }
339
+ try {
340
+ const result = await relayEthRequest({ publicClient, walletClient, account }, validated);
341
+ const body = {
342
+ ok: true,
343
+ txHash: result.txHash,
344
+ blockNumber: result.blockNumber.toString(),
345
+ };
346
+ return body;
347
+ }
348
+ catch (err) {
349
+ const name = err instanceof Error ? err.name : 'RelayBroadcastError';
350
+ // Same cause-chain diagnostic as /relay. Public on-chain data only —
351
+ // never signatures or amounts.
352
+ const chain = [];
353
+ let cursor = err;
354
+ let depth = 0;
355
+ while (cursor instanceof Error && depth < 6) {
356
+ const sm = cursor.shortMessage;
357
+ const frame = { name: cursor.name };
358
+ if (sm !== undefined)
359
+ frame.shortMessage = sm;
360
+ chain.push(frame);
361
+ cursor = cursor.cause;
362
+ depth++;
363
+ }
364
+ fastify.log.error({ chain }, 'relay-eth broadcast failed');
365
+ reply.status(502);
366
+ const body = { ok: false, error: name };
367
+ return body;
368
+ }
369
+ });
370
+ return fastify;
371
+ }
372
+ export async function startServer(cfg) {
373
+ const fastify = await buildServer(cfg);
374
+ await fastify.listen({ port: cfg.port, host: '127.0.0.1' });
375
+ return fastify;
376
+ }
377
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import type { Address, Hex } from 'viem';
2
+ /**
3
+ * Inbound relay request from a browser/agent.
4
+ *
5
+ * Privacy invariants:
6
+ * - The stealth address's PRIVATE key NEVER appears here. Only the EIP-2612
7
+ * permit signature (v, r, s, deadline) + the public stealth address.
8
+ * - No `amount` field — the contract reads `balanceOf(stealth)` on-chain.
9
+ * - The relayer service NEVER logs the full body.
10
+ */
11
+ export interface RelayRequest {
12
+ readonly token: Address;
13
+ readonly destination: Address;
14
+ readonly stealthAddress: Address;
15
+ readonly deadline: string;
16
+ readonly v: number;
17
+ readonly r: Hex;
18
+ readonly s: Hex;
19
+ }
20
+ export interface RelaySuccess {
21
+ readonly ok: true;
22
+ readonly txHash: Hex;
23
+ readonly blockNumber: string;
24
+ }
25
+ export interface RelayFailure {
26
+ readonly ok: false;
27
+ readonly error: string;
28
+ }
29
+ export type RelayResponse = RelaySuccess | RelayFailure;
30
+ export interface ServiceConfig {
31
+ readonly chainId: number;
32
+ readonly rpcUrl: string;
33
+ readonly relayerContract: Address;
34
+ readonly forwarderPrivateKey: Hex;
35
+ readonly allowedOrigins: readonly string[];
36
+ readonly port: number;
37
+ readonly rateLimitPerMinute: number;
38
+ /**
39
+ * When true, the /relay handler emits per-request preflight + EIP-712
40
+ * signature-recovery probe logs. These are valuable for debugging permit
41
+ * digest mismatches but produce one log line per request and surface
42
+ * (public) on-chain state. Off by default; enable only when troubleshooting.
43
+ */
44
+ readonly debugDiagnostics?: boolean;
45
+ /**
46
+ * Address of the ShroudFiEthRelayer contract for the P5.1 ETH gasless path.
47
+ * Used by /relay-eth as a defence-in-depth check that the inbound
48
+ * authorization's `address` field matches the deployed contract. When
49
+ * undefined, /relay-eth returns 501 Not Implemented.
50
+ */
51
+ readonly ethRelayerContract?: Address;
52
+ }
53
+ /**
54
+ * Inbound /relay-eth request. The shape mirrors what
55
+ * @shroud-fi/relayer's relayedSweepETH serialises on the SDK side.
56
+ *
57
+ * Privacy invariants:
58
+ * - The stealth EOA's PRIVATE key NEVER appears here. Only the public
59
+ * stealth address + two signatures (EIP-7702 SET_CODE auth + EIP-712
60
+ * EthSweep).
61
+ * - The signature is a spending-authorisation credential bound to the
62
+ * specific (destination, deadline, chainId, stealthEOA) tuple. Never
63
+ * logged in full; only the class name of any error appears in server
64
+ * logs.
65
+ * - No `amount` field — the contract reads address(this).balance under
66
+ * EIP-7702 delegation.
67
+ */
68
+ export interface RelayEthRequest {
69
+ readonly stealthAddress: Address;
70
+ readonly destination: Address;
71
+ readonly chainId: number;
72
+ readonly deadline: string;
73
+ readonly signature: Hex;
74
+ readonly authorization: {
75
+ readonly address: Address;
76
+ readonly chainId: number;
77
+ readonly nonce: number;
78
+ readonly r: Hex;
79
+ readonly s: Hex;
80
+ readonly yParity: 0 | 1;
81
+ };
82
+ }
83
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAEzC;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;IAChB,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,YAAY,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,mBAAmB,EAAE,GAAG,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CACvC;AAID;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC;IACxB,QAAQ,CAAC,aAAa,EAAE;QACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;QAChB,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;QAChB,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;KACzB,CAAC;CACH"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}