@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.
- package/LICENSE +21 -0
- package/README.md +64 -0
- package/dist/cjs/dev-entry.d.ts +24 -0
- package/dist/cjs/dev-entry.d.ts.map +1 -0
- package/dist/cjs/dev-entry.js +75 -0
- package/dist/cjs/dev-entry.js.map +1 -0
- package/dist/cjs/errors.d.ts +41 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +63 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/index.d.ts +8 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +26 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/prod-entry.d.ts +33 -0
- package/dist/cjs/prod-entry.d.ts.map +1 -0
- package/dist/cjs/prod-entry.js +98 -0
- package/dist/cjs/prod-entry.js.map +1 -0
- package/dist/cjs/relay-eth.d.ts +43 -0
- package/dist/cjs/relay-eth.d.ts.map +1 -0
- package/dist/cjs/relay-eth.js +95 -0
- package/dist/cjs/relay-eth.js.map +1 -0
- package/dist/cjs/relay.d.ts +38 -0
- package/dist/cjs/relay.d.ts.map +1 -0
- package/dist/cjs/relay.js +83 -0
- package/dist/cjs/relay.js.map +1 -0
- package/dist/cjs/server.d.ts +18 -0
- package/dist/cjs/server.d.ts.map +1 -0
- package/dist/cjs/server.js +384 -0
- package/dist/cjs/server.js.map +1 -0
- package/dist/cjs/types.d.ts +83 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/dev-entry.d.ts +24 -0
- package/dist/esm/dev-entry.d.ts.map +1 -0
- package/dist/esm/dev-entry.js +73 -0
- package/dist/esm/dev-entry.js.map +1 -0
- package/dist/esm/errors.d.ts +41 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +53 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.d.ts +8 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/prod-entry.d.ts +33 -0
- package/dist/esm/prod-entry.d.ts.map +1 -0
- package/dist/esm/prod-entry.js +96 -0
- package/dist/esm/prod-entry.js.map +1 -0
- package/dist/esm/relay-eth.d.ts +43 -0
- package/dist/esm/relay-eth.d.ts.map +1 -0
- package/dist/esm/relay-eth.js +91 -0
- package/dist/esm/relay-eth.js.map +1 -0
- package/dist/esm/relay.d.ts +38 -0
- package/dist/esm/relay.d.ts.map +1 -0
- package/dist/esm/relay.js +79 -0
- package/dist/esm/relay.js.map +1 -0
- package/dist/esm/server.d.ts +18 -0
- package/dist/esm/server.d.ts.map +1 -0
- package/dist/esm/server.js +377 -0
- package/dist/esm/server.js.map +1 -0
- package/dist/esm/types.d.ts +83 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +2 -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 +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ShroudFi contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @shroud-fi/self-host-relayer
|
|
2
|
+
|
|
3
|
+
> Self-hosted gasless sweep relayer service. Fastify server for EIP-7702 / ERC-2771 sweep broadcasts.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@shroud-fi/self-host-relayer)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm i @shroud-fi/self-host-relayer
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## What it does
|
|
13
|
+
|
|
14
|
+
`@shroud-fi/self-host-relayer` is a small Fastify HTTP server that operators can run to broadcast gasless sweep transactions for ShroudFi stealth addresses. It accepts signed authorizations from agents, broadcasts the on-chain tx, and returns a receipt.
|
|
15
|
+
|
|
16
|
+
This is the server-side counterpart to [`@shroud-fi/relayer`](https://www.npmjs.com/package/@shroud-fi/relayer). The reference deployment runs at `https://api.shroudfi.live`; this package lets you stand up your own.
|
|
17
|
+
|
|
18
|
+
## Routes
|
|
19
|
+
|
|
20
|
+
| Route | Method | Purpose |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| `/health` | GET | Liveness — returns `{ ok: true, chainId }`. |
|
|
23
|
+
| `/relay` | POST | ERC-20 sweep via EIP-2612 permit + ERC-2771 metaTx. |
|
|
24
|
+
| `/relay-eth` | POST | ETH sweep via EIP-7702 delegated authorization. |
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { startServer } from '@shroud-fi/self-host-relayer';
|
|
30
|
+
|
|
31
|
+
await startServer({
|
|
32
|
+
chainId: 8453,
|
|
33
|
+
port: 8789,
|
|
34
|
+
rpcUrl: process.env.BASE_RPC_URL!,
|
|
35
|
+
relayerContract: '0x44f35ED32516AE8247aF5ec4ED5d5BEb501631d1',
|
|
36
|
+
forwarderPrivateKey: process.env.FORWARDER_KEY! as `0x${string}`,
|
|
37
|
+
allowedOrigins: ['https://your-dapp.example'],
|
|
38
|
+
rateLimitPerMinute: 30,
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or use the production entry that reads everything from env:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
node node_modules/@shroud-fi/self-host-relayer/dist/esm/prod-entry.js
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Required env: `SHROUDFI_RELAYER_CONTRACT`, `SHROUDFI_RELAYER_FORWARDER_KEY`, `SHROUDFI_RELAYER_RPC_URL`, `SHROUDFI_RELAYER_ORIGINS`, `SHROUDFI_RELAYER_CHAIN_ID`, `SHROUDFI_RELAYER_PORT`. Optional: `SHROUDFI_RELAYER_RATE_LIMIT`, `SHROUDFI_ETH_RELAYER_CONTRACT`.
|
|
49
|
+
|
|
50
|
+
## Deployment
|
|
51
|
+
|
|
52
|
+
A reference PM2 ecosystem config + droplet ops playbook ships with the source repo. The relayer is designed to run as a **singleton fork** — cluster mode would race on the forwarder EOA nonce.
|
|
53
|
+
|
|
54
|
+
## Privacy invariants
|
|
55
|
+
|
|
56
|
+
- The relayer **never sees transfer amounts** in plaintext — sweep authorizations encode the destination + token, not the amount; the contract reads `balanceOf` at execution.
|
|
57
|
+
- **No request logging** by default. Set `DEBUG_DIAGNOSTICS=1` only for local debugging.
|
|
58
|
+
- **Single-purpose forwarder EOA** — should hold only the gas balance needed for sweep ops. Do not reuse with personal wallets.
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT — see [LICENSE](./LICENSE).
|
|
63
|
+
|
|
64
|
+
Part of the [ShroudFi](https://shroudfi.live) privacy SDK for AI agents on Base.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev entry: reads config from env vars and starts the server.
|
|
3
|
+
*
|
|
4
|
+
* Required env:
|
|
5
|
+
* SHROUDFI_RELAYER_CONTRACT - deployed ShroudFiRelayer address
|
|
6
|
+
* SHROUDFI_RELAYER_FORWARDER_KEY - 0x-prefixed 32-byte forwarder EOA key
|
|
7
|
+
* SHROUDFI_RELAYER_RPC_URL - chain RPC URL
|
|
8
|
+
*
|
|
9
|
+
* Optional env:
|
|
10
|
+
* SHROUDFI_RELAYER_CHAIN_ID - default 84532
|
|
11
|
+
* SHROUDFI_RELAYER_PORT - default 8787
|
|
12
|
+
* SHROUDFI_RELAYER_ORIGINS - comma-sep allowed origins (default localhost demo)
|
|
13
|
+
* SHROUDFI_RELAYER_RATE_LIMIT - per-IP requests per minute (default 30)
|
|
14
|
+
* SHROUDFI_ETH_RELAYER_CONTRACT - deployed ShroudFiEthRelayer address.
|
|
15
|
+
* When set, /relay-eth becomes active for
|
|
16
|
+
* gasless ETH sweeps via EIP-7702. When
|
|
17
|
+
* unset, /relay-eth returns 501.
|
|
18
|
+
*
|
|
19
|
+
* Privacy: this file is the ONLY place that touches process.env for the
|
|
20
|
+
* forwarder key. Once read, the key flows into a viem account closure inside
|
|
21
|
+
* the server and never resurfaces.
|
|
22
|
+
*/
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=dev-entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-entry.d.ts","sourceRoot":"","sources":["../../src/dev-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Dev entry: reads config from env vars and starts the server.
|
|
4
|
+
*
|
|
5
|
+
* Required env:
|
|
6
|
+
* SHROUDFI_RELAYER_CONTRACT - deployed ShroudFiRelayer address
|
|
7
|
+
* SHROUDFI_RELAYER_FORWARDER_KEY - 0x-prefixed 32-byte forwarder EOA key
|
|
8
|
+
* SHROUDFI_RELAYER_RPC_URL - chain RPC URL
|
|
9
|
+
*
|
|
10
|
+
* Optional env:
|
|
11
|
+
* SHROUDFI_RELAYER_CHAIN_ID - default 84532
|
|
12
|
+
* SHROUDFI_RELAYER_PORT - default 8787
|
|
13
|
+
* SHROUDFI_RELAYER_ORIGINS - comma-sep allowed origins (default localhost demo)
|
|
14
|
+
* SHROUDFI_RELAYER_RATE_LIMIT - per-IP requests per minute (default 30)
|
|
15
|
+
* SHROUDFI_ETH_RELAYER_CONTRACT - deployed ShroudFiEthRelayer address.
|
|
16
|
+
* When set, /relay-eth becomes active for
|
|
17
|
+
* gasless ETH sweeps via EIP-7702. When
|
|
18
|
+
* unset, /relay-eth returns 501.
|
|
19
|
+
*
|
|
20
|
+
* Privacy: this file is the ONLY place that touches process.env for the
|
|
21
|
+
* forwarder key. Once read, the key flows into a viem account closure inside
|
|
22
|
+
* the server and never resurfaces.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
const server_js_1 = require("./server.js");
|
|
26
|
+
const errors_js_1 = require("./errors.js");
|
|
27
|
+
function envOrThrow(name) {
|
|
28
|
+
const v = process.env[name];
|
|
29
|
+
if (v === undefined || v.trim().length === 0) {
|
|
30
|
+
throw new errors_js_1.RelayConfigError(name);
|
|
31
|
+
}
|
|
32
|
+
return v.trim();
|
|
33
|
+
}
|
|
34
|
+
function loadConfig() {
|
|
35
|
+
const chainId = Number(process.env.SHROUDFI_RELAYER_CHAIN_ID?.trim() ?? '84532');
|
|
36
|
+
const port = Number(process.env.SHROUDFI_RELAYER_PORT?.trim() ?? '8787');
|
|
37
|
+
const rateLimitPerMinute = Number(process.env.SHROUDFI_RELAYER_RATE_LIMIT?.trim() ?? '30');
|
|
38
|
+
const originsCsv = process.env.SHROUDFI_RELAYER_ORIGINS?.trim() ??
|
|
39
|
+
'http://localhost:3000,http://localhost:3001,http://localhost:3002,http://localhost:3003,http://localhost:3005,http://localhost:3006,http://localhost:3008,http://localhost:3009,http://127.0.0.1:3005,http://127.0.0.1:3009';
|
|
40
|
+
const allowedOrigins = originsCsv
|
|
41
|
+
.split(',')
|
|
42
|
+
.map((s) => s.trim())
|
|
43
|
+
.filter((s) => s.length > 0);
|
|
44
|
+
const ethRelayerRaw = process.env.SHROUDFI_ETH_RELAYER_CONTRACT?.trim();
|
|
45
|
+
const ethRelayerContract = ethRelayerRaw !== undefined && ethRelayerRaw.length > 0
|
|
46
|
+
? ethRelayerRaw
|
|
47
|
+
: undefined;
|
|
48
|
+
const base = {
|
|
49
|
+
chainId,
|
|
50
|
+
port,
|
|
51
|
+
rateLimitPerMinute,
|
|
52
|
+
allowedOrigins,
|
|
53
|
+
relayerContract: envOrThrow('SHROUDFI_RELAYER_CONTRACT'),
|
|
54
|
+
rpcUrl: envOrThrow('SHROUDFI_RELAYER_RPC_URL'),
|
|
55
|
+
forwarderPrivateKey: envOrThrow('SHROUDFI_RELAYER_FORWARDER_KEY'),
|
|
56
|
+
};
|
|
57
|
+
return ethRelayerContract === undefined
|
|
58
|
+
? base
|
|
59
|
+
: { ...base, ethRelayerContract };
|
|
60
|
+
}
|
|
61
|
+
async function main() {
|
|
62
|
+
const cfg = loadConfig();
|
|
63
|
+
await (0, server_js_1.startServer)(cfg);
|
|
64
|
+
// Print a single line so the operator knows the service is up. NO key,
|
|
65
|
+
// NO RPC URL, NO contract address (those are public but we keep the
|
|
66
|
+
// surface minimal — they can be queried via /health).
|
|
67
|
+
// eslint-disable-next-line no-console
|
|
68
|
+
console.log(`self-host-relayer listening on http://127.0.0.1:${cfg.port} (chain ${cfg.chainId})`);
|
|
69
|
+
}
|
|
70
|
+
void main().catch((err) => {
|
|
71
|
+
// eslint-disable-next-line no-console
|
|
72
|
+
console.error(err instanceof Error ? err.name : 'StartupError');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=dev-entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-entry.js","sourceRoot":"","sources":["../../src/dev-entry.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;AAEH,2CAA0C;AAC1C,2CAA+C;AAI/C,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,4BAAgB,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;AAClB,CAAC;AAED,SAAS,UAAU;IACjB,MAAM,OAAO,GAAG,MAAM,CACpB,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,IAAI,EAAE,IAAI,OAAO,CACzD,CAAC;IACF,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,IAAI,EAAE,IAAI,MAAM,CAAC,CAAC;IACzE,MAAM,kBAAkB,GAAG,MAAM,CAC/B,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,IAAI,EAAE,IAAI,IAAI,CACxD,CAAC;IACF,MAAM,UAAU,GACd,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,IAAI,EAAE;QAC5C,6NAA6N,CAAC;IAChO,MAAM,cAAc,GAAG,UAAU;SAC9B,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE/B,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,6BAA6B,EAAE,IAAI,EAAE,CAAC;IACxE,MAAM,kBAAkB,GACtB,aAAa,KAAK,SAAS,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC;QACrD,CAAC,CAAE,aAAyB;QAC5B,CAAC,CAAC,SAAS,CAAC;IAEhB,MAAM,IAAI,GAAG;QACX,OAAO;QACP,IAAI;QACJ,kBAAkB;QAClB,cAAc;QACd,eAAe,EAAE,UAAU,CAAC,2BAA2B,CAAY;QACnE,MAAM,EAAE,UAAU,CAAC,0BAA0B,CAAC;QAC9C,mBAAmB,EAAE,UAAU,CAC7B,gCAAgC,CAC1B;KACT,CAAC;IACF,OAAO,kBAAkB,KAAK,SAAS;QACrC,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,kBAAkB,EAAE,CAAC;AACtC,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,MAAM,IAAA,uBAAW,EAAC,GAAG,CAAC,CAAC;IACvB,uEAAuE;IACvE,oEAAoE;IACpE,sDAAsD;IACtD,sCAAsC;IACtC,OAAO,CAAC,GAAG,CACT,mDAAmD,GAAG,CAAC,IAAI,WAAW,GAAG,CAAC,OAAO,GAAG,CACrF,CAAC;AACJ,CAAC;AAED,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IACjC,sCAAsC;IACtC,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Privacy-safe errors for @shroud-fi/self-host-relayer.
|
|
3
|
+
*
|
|
4
|
+
* Invariants mirrored from other packages:
|
|
5
|
+
* - No key bytes in messages
|
|
6
|
+
* - No amount values in messages
|
|
7
|
+
* - No private signature bytes in messages
|
|
8
|
+
*/
|
|
9
|
+
export declare class SelfHostRelayerError extends Error {
|
|
10
|
+
readonly name: string;
|
|
11
|
+
constructor(message: string, options?: {
|
|
12
|
+
cause?: unknown;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export declare class InvalidRelayRequestError extends SelfHostRelayerError {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
constructor();
|
|
18
|
+
}
|
|
19
|
+
export declare class RelayBroadcastError extends SelfHostRelayerError {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
constructor(underlyingName?: string, options?: {
|
|
22
|
+
cause?: unknown;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export declare class RelayConfigError extends SelfHostRelayerError {
|
|
26
|
+
readonly name: string;
|
|
27
|
+
constructor(field: string);
|
|
28
|
+
}
|
|
29
|
+
export declare class InvalidRelayEthRequestError extends SelfHostRelayerError {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
constructor();
|
|
32
|
+
}
|
|
33
|
+
export declare class EthRelayerNotConfiguredError extends SelfHostRelayerError {
|
|
34
|
+
readonly name: string;
|
|
35
|
+
constructor();
|
|
36
|
+
}
|
|
37
|
+
export declare class AuthorizationContractMismatchError extends SelfHostRelayerError {
|
|
38
|
+
readonly name: string;
|
|
39
|
+
constructor();
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,SAAkB,IAAI,EAAE,MAAM,CAA0B;gBAC5C,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAG3D;AAED,qBAAa,wBAAyB,SAAQ,oBAAoB;IAChE,SAAkB,IAAI,EAAE,MAAM,CAA8B;;CAI7D;AAED,qBAAa,mBAAoB,SAAQ,oBAAoB;IAC3D,SAAkB,IAAI,EAAE,MAAM,CAAyB;gBAC3C,cAAc,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAQnE;AAED,qBAAa,gBAAiB,SAAQ,oBAAoB;IACxD,SAAkB,IAAI,EAAE,MAAM,CAAsB;gBACxC,KAAK,EAAE,MAAM;CAG1B;AAED,qBAAa,2BAA4B,SAAQ,oBAAoB;IACnE,SAAkB,IAAI,EAAE,MAAM,CAAiC;;CAIhE;AAED,qBAAa,4BAA6B,SAAQ,oBAAoB;IACpE,SAAkB,IAAI,EAAE,MAAM,CAAkC;;CAIjE;AAED,qBAAa,kCAAmC,SAAQ,oBAAoB;IAC1E,SAAkB,IAAI,EAAE,MAAM,CAAwC;;CAIvE"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Privacy-safe errors for @shroud-fi/self-host-relayer.
|
|
4
|
+
*
|
|
5
|
+
* Invariants mirrored from other packages:
|
|
6
|
+
* - No key bytes in messages
|
|
7
|
+
* - No amount values in messages
|
|
8
|
+
* - No private signature bytes in messages
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.AuthorizationContractMismatchError = exports.EthRelayerNotConfiguredError = exports.InvalidRelayEthRequestError = exports.RelayConfigError = exports.RelayBroadcastError = exports.InvalidRelayRequestError = exports.SelfHostRelayerError = void 0;
|
|
12
|
+
class SelfHostRelayerError extends Error {
|
|
13
|
+
name = 'SelfHostRelayerError';
|
|
14
|
+
constructor(message, options) {
|
|
15
|
+
super(message, options);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.SelfHostRelayerError = SelfHostRelayerError;
|
|
19
|
+
class InvalidRelayRequestError extends SelfHostRelayerError {
|
|
20
|
+
name = 'InvalidRelayRequestError';
|
|
21
|
+
constructor() {
|
|
22
|
+
super('Relay request body failed validation');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.InvalidRelayRequestError = InvalidRelayRequestError;
|
|
26
|
+
class RelayBroadcastError extends SelfHostRelayerError {
|
|
27
|
+
name = 'RelayBroadcastError';
|
|
28
|
+
constructor(underlyingName, options) {
|
|
29
|
+
super(underlyingName !== undefined
|
|
30
|
+
? `Broadcast failed (${underlyingName})`
|
|
31
|
+
: 'Broadcast failed', options);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.RelayBroadcastError = RelayBroadcastError;
|
|
35
|
+
class RelayConfigError extends SelfHostRelayerError {
|
|
36
|
+
name = 'RelayConfigError';
|
|
37
|
+
constructor(field) {
|
|
38
|
+
super(`Relayer config missing required field: ${field}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.RelayConfigError = RelayConfigError;
|
|
42
|
+
class InvalidRelayEthRequestError extends SelfHostRelayerError {
|
|
43
|
+
name = 'InvalidRelayEthRequestError';
|
|
44
|
+
constructor() {
|
|
45
|
+
super('Relay ETH request body failed validation');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.InvalidRelayEthRequestError = InvalidRelayEthRequestError;
|
|
49
|
+
class EthRelayerNotConfiguredError extends SelfHostRelayerError {
|
|
50
|
+
name = 'EthRelayerNotConfiguredError';
|
|
51
|
+
constructor() {
|
|
52
|
+
super('ETH relayer contract address not configured');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.EthRelayerNotConfiguredError = EthRelayerNotConfiguredError;
|
|
56
|
+
class AuthorizationContractMismatchError extends SelfHostRelayerError {
|
|
57
|
+
name = 'AuthorizationContractMismatchError';
|
|
58
|
+
constructor() {
|
|
59
|
+
super('EIP-7702 authorization address does not match configured ETH relayer');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.AuthorizationContractMismatchError = AuthorizationContractMismatchError;
|
|
63
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,MAAa,oBAAqB,SAAQ,KAAK;IAC3B,IAAI,GAAW,sBAAsB,CAAC;IACxD,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC1B,CAAC;CACF;AALD,oDAKC;AAED,MAAa,wBAAyB,SAAQ,oBAAoB;IAC9C,IAAI,GAAW,0BAA0B,CAAC;IAC5D;QACE,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAChD,CAAC;CACF;AALD,4DAKC;AAED,MAAa,mBAAoB,SAAQ,oBAAoB;IACzC,IAAI,GAAW,qBAAqB,CAAC;IACvD,YAAY,cAAuB,EAAE,OAA6B;QAChE,KAAK,CACH,cAAc,KAAK,SAAS;YAC1B,CAAC,CAAC,qBAAqB,cAAc,GAAG;YACxC,CAAC,CAAC,kBAAkB,EACtB,OAAO,CACR,CAAC;IACJ,CAAC;CACF;AAVD,kDAUC;AAED,MAAa,gBAAiB,SAAQ,oBAAoB;IACtC,IAAI,GAAW,kBAAkB,CAAC;IACpD,YAAY,KAAa;QACvB,KAAK,CAAC,0CAA0C,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC;CACF;AALD,4CAKC;AAED,MAAa,2BAA4B,SAAQ,oBAAoB;IACjD,IAAI,GAAW,6BAA6B,CAAC;IAC/D;QACE,KAAK,CAAC,0CAA0C,CAAC,CAAC;IACpD,CAAC;CACF;AALD,kEAKC;AAED,MAAa,4BAA6B,SAAQ,oBAAoB;IAClD,IAAI,GAAW,8BAA8B,CAAC;IAChE;QACE,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACvD,CAAC;CACF;AALD,oEAKC;AAED,MAAa,kCAAmC,SAAQ,oBAAoB;IACxD,IAAI,GAAW,oCAAoC,CAAC;IACtE;QACE,KAAK,CAAC,sEAAsE,CAAC,CAAC;IAChF,CAAC;CACF;AALD,gFAKC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { buildServer, startServer } from './server.js';
|
|
2
|
+
export { buildRelayCalldata, relayRequest } from './relay.js';
|
|
3
|
+
export type { RelayCtx, RelayResult } from './relay.js';
|
|
4
|
+
export { buildEthSweepCalldata, relayEthRequest, } from './relay-eth.js';
|
|
5
|
+
export type { RelayEthCtx, RelayEthResult } from './relay-eth.js';
|
|
6
|
+
export type { RelayRequest, RelayResponse, RelaySuccess, RelayFailure, ServiceConfig, RelayEthRequest, } from './types.js';
|
|
7
|
+
export { SelfHostRelayerError, InvalidRelayRequestError, RelayBroadcastError, RelayConfigError, InvalidRelayEthRequestError, EthRelayerNotConfiguredError, AuthorizationContractMismatchError, } from './errors.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC9D,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EACL,qBAAqB,EACrB,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAClE,YAAY,EACV,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,gBAAgB,EAChB,2BAA2B,EAC3B,4BAA4B,EAC5B,kCAAkC,GACnC,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Public surface — @shroud-fi/self-host-relayer
|
|
3
|
+
//
|
|
4
|
+
// Exports the server builder + the pure relay-logic helpers so the same
|
|
5
|
+
// package can be embedded in another Node process or run standalone via
|
|
6
|
+
// `pnpm dev`.
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.AuthorizationContractMismatchError = exports.EthRelayerNotConfiguredError = exports.InvalidRelayEthRequestError = exports.RelayConfigError = exports.RelayBroadcastError = exports.InvalidRelayRequestError = exports.SelfHostRelayerError = exports.relayEthRequest = exports.buildEthSweepCalldata = exports.relayRequest = exports.buildRelayCalldata = exports.startServer = exports.buildServer = void 0;
|
|
9
|
+
var server_js_1 = require("./server.js");
|
|
10
|
+
Object.defineProperty(exports, "buildServer", { enumerable: true, get: function () { return server_js_1.buildServer; } });
|
|
11
|
+
Object.defineProperty(exports, "startServer", { enumerable: true, get: function () { return server_js_1.startServer; } });
|
|
12
|
+
var relay_js_1 = require("./relay.js");
|
|
13
|
+
Object.defineProperty(exports, "buildRelayCalldata", { enumerable: true, get: function () { return relay_js_1.buildRelayCalldata; } });
|
|
14
|
+
Object.defineProperty(exports, "relayRequest", { enumerable: true, get: function () { return relay_js_1.relayRequest; } });
|
|
15
|
+
var relay_eth_js_1 = require("./relay-eth.js");
|
|
16
|
+
Object.defineProperty(exports, "buildEthSweepCalldata", { enumerable: true, get: function () { return relay_eth_js_1.buildEthSweepCalldata; } });
|
|
17
|
+
Object.defineProperty(exports, "relayEthRequest", { enumerable: true, get: function () { return relay_eth_js_1.relayEthRequest; } });
|
|
18
|
+
var errors_js_1 = require("./errors.js");
|
|
19
|
+
Object.defineProperty(exports, "SelfHostRelayerError", { enumerable: true, get: function () { return errors_js_1.SelfHostRelayerError; } });
|
|
20
|
+
Object.defineProperty(exports, "InvalidRelayRequestError", { enumerable: true, get: function () { return errors_js_1.InvalidRelayRequestError; } });
|
|
21
|
+
Object.defineProperty(exports, "RelayBroadcastError", { enumerable: true, get: function () { return errors_js_1.RelayBroadcastError; } });
|
|
22
|
+
Object.defineProperty(exports, "RelayConfigError", { enumerable: true, get: function () { return errors_js_1.RelayConfigError; } });
|
|
23
|
+
Object.defineProperty(exports, "InvalidRelayEthRequestError", { enumerable: true, get: function () { return errors_js_1.InvalidRelayEthRequestError; } });
|
|
24
|
+
Object.defineProperty(exports, "EthRelayerNotConfiguredError", { enumerable: true, get: function () { return errors_js_1.EthRelayerNotConfiguredError; } });
|
|
25
|
+
Object.defineProperty(exports, "AuthorizationContractMismatchError", { enumerable: true, get: function () { return errors_js_1.AuthorizationContractMismatchError; } });
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA,gDAAgD;AAChD,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,cAAc;;;AAEd,yCAAuD;AAA9C,wGAAA,WAAW,OAAA;AAAE,wGAAA,WAAW,OAAA;AACjC,uCAA8D;AAArD,8GAAA,kBAAkB,OAAA;AAAE,wGAAA,YAAY,OAAA;AAEzC,+CAGwB;AAFtB,qHAAA,qBAAqB,OAAA;AACrB,+GAAA,eAAe,OAAA;AAWjB,yCAQqB;AAPnB,iHAAA,oBAAoB,OAAA;AACpB,qHAAA,wBAAwB,OAAA;AACxB,gHAAA,mBAAmB,OAAA;AACnB,6GAAA,gBAAgB,OAAA;AAChB,wHAAA,2BAA2B,OAAA;AAC3B,yHAAA,4BAA4B,OAAA;AAC5B,+HAAA,kCAAkC,OAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production entry: strict env, fail-fast on missing required values, no
|
|
3
|
+
* permissive defaults that could mask misconfiguration in deployment.
|
|
4
|
+
*
|
|
5
|
+
* Required env (no defaults — startup fails if any are missing):
|
|
6
|
+
* SHROUDFI_RELAYER_CONTRACT - deployed ShroudFiRelayer address
|
|
7
|
+
* SHROUDFI_RELAYER_FORWARDER_KEY - 0x-prefixed 32-byte forwarder EOA key
|
|
8
|
+
* SHROUDFI_RELAYER_RPC_URL - chain RPC URL
|
|
9
|
+
* SHROUDFI_RELAYER_ORIGINS - comma-sep allowed origins (e.g. https://app.shroudfi.live)
|
|
10
|
+
* SHROUDFI_RELAYER_CHAIN_ID - chain id (84532 = Base Sepolia, 8453 = Base)
|
|
11
|
+
* SHROUDFI_RELAYER_PORT - bind port (typically 8789 on droplet)
|
|
12
|
+
*
|
|
13
|
+
* Optional env:
|
|
14
|
+
* SHROUDFI_RELAYER_RATE_LIMIT - per-IP requests per minute (default 30)
|
|
15
|
+
* SHROUDFI_ETH_RELAYER_CONTRACT - deployed ShroudFiEthRelayer address.
|
|
16
|
+
* When set, /relay-eth becomes active for
|
|
17
|
+
* gasless ETH sweeps via EIP-7702. When
|
|
18
|
+
* unset, /relay-eth returns 501.
|
|
19
|
+
* DEBUG_DIAGNOSTICS - "1" to enable per-request preflight + sig recovery logs
|
|
20
|
+
*
|
|
21
|
+
* Differences from dev-entry.ts:
|
|
22
|
+
* - Every required field uses envOrThrow; no fallbacks
|
|
23
|
+
* - SHROUDFI_RELAYER_ORIGINS is REQUIRED (dev defaults to localhost; prod must be explicit)
|
|
24
|
+
* - No startup log line printed to stdout — only journald via PM2 captures fastify's
|
|
25
|
+
* own "Server listening at ..." line
|
|
26
|
+
* - DEBUG_DIAGNOSTICS env opts into the per-request debug logs (off by default)
|
|
27
|
+
*
|
|
28
|
+
* Privacy: this file is the ONLY place that touches process.env for the
|
|
29
|
+
* forwarder key. Once read, the key flows into a viem account closure inside
|
|
30
|
+
* the server and never resurfaces.
|
|
31
|
+
*/
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=prod-entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prod-entry.d.ts","sourceRoot":"","sources":["../../src/prod-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Production entry: strict env, fail-fast on missing required values, no
|
|
4
|
+
* permissive defaults that could mask misconfiguration in deployment.
|
|
5
|
+
*
|
|
6
|
+
* Required env (no defaults — startup fails if any are missing):
|
|
7
|
+
* SHROUDFI_RELAYER_CONTRACT - deployed ShroudFiRelayer address
|
|
8
|
+
* SHROUDFI_RELAYER_FORWARDER_KEY - 0x-prefixed 32-byte forwarder EOA key
|
|
9
|
+
* SHROUDFI_RELAYER_RPC_URL - chain RPC URL
|
|
10
|
+
* SHROUDFI_RELAYER_ORIGINS - comma-sep allowed origins (e.g. https://app.shroudfi.live)
|
|
11
|
+
* SHROUDFI_RELAYER_CHAIN_ID - chain id (84532 = Base Sepolia, 8453 = Base)
|
|
12
|
+
* SHROUDFI_RELAYER_PORT - bind port (typically 8789 on droplet)
|
|
13
|
+
*
|
|
14
|
+
* Optional env:
|
|
15
|
+
* SHROUDFI_RELAYER_RATE_LIMIT - per-IP requests per minute (default 30)
|
|
16
|
+
* SHROUDFI_ETH_RELAYER_CONTRACT - deployed ShroudFiEthRelayer address.
|
|
17
|
+
* When set, /relay-eth becomes active for
|
|
18
|
+
* gasless ETH sweeps via EIP-7702. When
|
|
19
|
+
* unset, /relay-eth returns 501.
|
|
20
|
+
* DEBUG_DIAGNOSTICS - "1" to enable per-request preflight + sig recovery logs
|
|
21
|
+
*
|
|
22
|
+
* Differences from dev-entry.ts:
|
|
23
|
+
* - Every required field uses envOrThrow; no fallbacks
|
|
24
|
+
* - SHROUDFI_RELAYER_ORIGINS is REQUIRED (dev defaults to localhost; prod must be explicit)
|
|
25
|
+
* - No startup log line printed to stdout — only journald via PM2 captures fastify's
|
|
26
|
+
* own "Server listening at ..." line
|
|
27
|
+
* - DEBUG_DIAGNOSTICS env opts into the per-request debug logs (off by default)
|
|
28
|
+
*
|
|
29
|
+
* Privacy: this file is the ONLY place that touches process.env for the
|
|
30
|
+
* forwarder key. Once read, the key flows into a viem account closure inside
|
|
31
|
+
* the server and never resurfaces.
|
|
32
|
+
*/
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
const server_js_1 = require("./server.js");
|
|
35
|
+
const errors_js_1 = require("./errors.js");
|
|
36
|
+
function envOrThrow(name) {
|
|
37
|
+
const v = process.env[name];
|
|
38
|
+
if (v === undefined || v.trim().length === 0) {
|
|
39
|
+
throw new errors_js_1.RelayConfigError(name);
|
|
40
|
+
}
|
|
41
|
+
return v.trim();
|
|
42
|
+
}
|
|
43
|
+
function loadConfig() {
|
|
44
|
+
const chainId = Number(envOrThrow('SHROUDFI_RELAYER_CHAIN_ID'));
|
|
45
|
+
if (!Number.isFinite(chainId) || chainId <= 0) {
|
|
46
|
+
throw new errors_js_1.RelayConfigError('SHROUDFI_RELAYER_CHAIN_ID');
|
|
47
|
+
}
|
|
48
|
+
const port = Number(envOrThrow('SHROUDFI_RELAYER_PORT'));
|
|
49
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
50
|
+
throw new errors_js_1.RelayConfigError('SHROUDFI_RELAYER_PORT');
|
|
51
|
+
}
|
|
52
|
+
const rateLimitRaw = process.env.SHROUDFI_RELAYER_RATE_LIMIT?.trim();
|
|
53
|
+
const rateLimitPerMinute = rateLimitRaw && rateLimitRaw.length > 0 ? Number(rateLimitRaw) : 30;
|
|
54
|
+
if (!Number.isFinite(rateLimitPerMinute) || rateLimitPerMinute <= 0) {
|
|
55
|
+
throw new errors_js_1.RelayConfigError('SHROUDFI_RELAYER_RATE_LIMIT');
|
|
56
|
+
}
|
|
57
|
+
const originsCsv = envOrThrow('SHROUDFI_RELAYER_ORIGINS');
|
|
58
|
+
const allowedOrigins = originsCsv
|
|
59
|
+
.split(',')
|
|
60
|
+
.map((s) => s.trim())
|
|
61
|
+
.filter((s) => s.length > 0);
|
|
62
|
+
if (allowedOrigins.length === 0) {
|
|
63
|
+
throw new errors_js_1.RelayConfigError('SHROUDFI_RELAYER_ORIGINS');
|
|
64
|
+
}
|
|
65
|
+
const debugDiagnostics = process.env.DEBUG_DIAGNOSTICS === '1';
|
|
66
|
+
const ethRelayerRaw = process.env.SHROUDFI_ETH_RELAYER_CONTRACT?.trim();
|
|
67
|
+
const ethRelayerContract = ethRelayerRaw !== undefined && ethRelayerRaw.length > 0
|
|
68
|
+
? ethRelayerRaw
|
|
69
|
+
: undefined;
|
|
70
|
+
const base = {
|
|
71
|
+
chainId,
|
|
72
|
+
port,
|
|
73
|
+
rateLimitPerMinute,
|
|
74
|
+
allowedOrigins,
|
|
75
|
+
relayerContract: envOrThrow('SHROUDFI_RELAYER_CONTRACT'),
|
|
76
|
+
rpcUrl: envOrThrow('SHROUDFI_RELAYER_RPC_URL'),
|
|
77
|
+
forwarderPrivateKey: envOrThrow('SHROUDFI_RELAYER_FORWARDER_KEY'),
|
|
78
|
+
debugDiagnostics,
|
|
79
|
+
};
|
|
80
|
+
return ethRelayerContract === undefined
|
|
81
|
+
? base
|
|
82
|
+
: { ...base, ethRelayerContract };
|
|
83
|
+
}
|
|
84
|
+
async function main() {
|
|
85
|
+
const cfg = loadConfig();
|
|
86
|
+
await (0, server_js_1.startServer)(cfg);
|
|
87
|
+
// Intentionally no stdout — Fastify's logger already emits a "Server
|
|
88
|
+
// listening at ..." line that PM2/journald captures. Avoid extra surface.
|
|
89
|
+
}
|
|
90
|
+
void main().catch((err) => {
|
|
91
|
+
// Startup failure → emit only the error class name to stderr, then exit 1.
|
|
92
|
+
// PM2 will record this and (per ecosystem config) attempt restart up to
|
|
93
|
+
// max_restarts before giving up.
|
|
94
|
+
// eslint-disable-next-line no-console
|
|
95
|
+
console.error(err instanceof Error ? err.name : 'StartupError');
|
|
96
|
+
process.exit(1);
|
|
97
|
+
});
|
|
98
|
+
//# sourceMappingURL=prod-entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prod-entry.js","sourceRoot":"","sources":["../../src/prod-entry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ETH gasless sweep relay — broadcasts an EIP-7702 type-0x04 tx that delegates
|
|
3
|
+
* the stealth EOA to ShroudFiEthRelayer and calls sweepETH(destination,
|
|
4
|
+
* deadline, signature).
|
|
5
|
+
*
|
|
6
|
+
* Privacy invariants:
|
|
7
|
+
* - Relayer EOA's private key held in the viem account closure. Never
|
|
8
|
+
* logged or attached to thrown errors.
|
|
9
|
+
* - The EIP-712 signature in the request is a one-time spending
|
|
10
|
+
* authorization bound to (destination, deadline, chainId, stealthEOA).
|
|
11
|
+
* We pass it through verbatim — never store or re-sign.
|
|
12
|
+
* - No amount fields anywhere in the request or response.
|
|
13
|
+
*/
|
|
14
|
+
import { type Account, type Chain, type Hex, type PublicClient, type Transport, type WalletClient } from 'viem';
|
|
15
|
+
import type { RelayEthRequest } from './types.js';
|
|
16
|
+
/**
|
|
17
|
+
* Same generic-loose shape as RelayCtx. Holds the broadcast-side viem clients
|
|
18
|
+
* plus the relayer EOA. The relayer EOA pays gas for the outer tx.
|
|
19
|
+
*/
|
|
20
|
+
export interface RelayEthCtx {
|
|
21
|
+
readonly publicClient: PublicClient<Transport, Chain | undefined>;
|
|
22
|
+
readonly walletClient: WalletClient<Transport, Chain | undefined, Account>;
|
|
23
|
+
readonly account: Account;
|
|
24
|
+
}
|
|
25
|
+
export interface RelayEthResult {
|
|
26
|
+
readonly txHash: Hex;
|
|
27
|
+
readonly blockNumber: bigint;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build the inner sweepETH calldata. The OUTER tx is the type-0x04 transaction
|
|
31
|
+
* itself which carries the authorization list and the `to: stealthAddress`;
|
|
32
|
+
* the inner data is what executes once the EOA's code is set to the relayer.
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildEthSweepCalldata(req: RelayEthRequest): Hex;
|
|
35
|
+
/**
|
|
36
|
+
* Submit the type-0x04 tx and wait for receipt.
|
|
37
|
+
*
|
|
38
|
+
* On any viem error we wrap with RelayBroadcastError(name, { cause: err }) so
|
|
39
|
+
* the server can walk the cause chain and log the actual revert reason
|
|
40
|
+
* (public on-chain data) without surfacing it to clients.
|
|
41
|
+
*/
|
|
42
|
+
export declare function relayEthRequest(ctx: RelayEthCtx, req: RelayEthRequest): Promise<RelayEthResult>;
|
|
43
|
+
//# sourceMappingURL=relay-eth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay-eth.d.ts","sourceRoot":"","sources":["../../src/relay-eth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAEL,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,GAAG,EACR,KAAK,YAAY,EAEjB,KAAK,SAAS,EACd,KAAK,YAAY,EAClB,MAAM,MAAM,CAAC;AAEd,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAgBlD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC;IAClE,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,GAAG,SAAS,EAAE,OAAO,CAAC,CAAC;IAC3E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAmBD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,GAAG,CAM/D;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,cAAc,CAAC,CAyBzB"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ETH gasless sweep relay — broadcasts an EIP-7702 type-0x04 tx that delegates
|
|
4
|
+
* the stealth EOA to ShroudFiEthRelayer and calls sweepETH(destination,
|
|
5
|
+
* deadline, signature).
|
|
6
|
+
*
|
|
7
|
+
* Privacy invariants:
|
|
8
|
+
* - Relayer EOA's private key held in the viem account closure. Never
|
|
9
|
+
* logged or attached to thrown errors.
|
|
10
|
+
* - The EIP-712 signature in the request is a one-time spending
|
|
11
|
+
* authorization bound to (destination, deadline, chainId, stealthEOA).
|
|
12
|
+
* We pass it through verbatim — never store or re-sign.
|
|
13
|
+
* - No amount fields anywhere in the request or response.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.buildEthSweepCalldata = buildEthSweepCalldata;
|
|
17
|
+
exports.relayEthRequest = relayEthRequest;
|
|
18
|
+
const viem_1 = require("viem");
|
|
19
|
+
const errors_js_1 = require("./errors.js");
|
|
20
|
+
const SWEEP_ETH_ABI = [
|
|
21
|
+
{
|
|
22
|
+
type: 'function',
|
|
23
|
+
name: 'sweepETH',
|
|
24
|
+
stateMutability: 'nonpayable',
|
|
25
|
+
inputs: [
|
|
26
|
+
{ name: 'destination', type: 'address' },
|
|
27
|
+
{ name: 'deadline', type: 'uint256' },
|
|
28
|
+
{ name: 'signature', type: 'bytes' },
|
|
29
|
+
],
|
|
30
|
+
outputs: [],
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Reconstruct a viem `SignedAuthorization` from the wire-encoded fields. The
|
|
35
|
+
* fields are exactly what the SDK serialises in @shroud-fi/relayer's
|
|
36
|
+
* eth-sweep.ts — `nonce` arrives as a number, `chainId` as a number, `r`/`s`
|
|
37
|
+
* as 0x-prefixed hex, `yParity` as 0 or 1.
|
|
38
|
+
*/
|
|
39
|
+
function reconstructAuthorization(req) {
|
|
40
|
+
return {
|
|
41
|
+
address: req.authorization.address,
|
|
42
|
+
chainId: req.authorization.chainId,
|
|
43
|
+
nonce: req.authorization.nonce,
|
|
44
|
+
r: req.authorization.r,
|
|
45
|
+
s: req.authorization.s,
|
|
46
|
+
yParity: req.authorization.yParity,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Build the inner sweepETH calldata. The OUTER tx is the type-0x04 transaction
|
|
51
|
+
* itself which carries the authorization list and the `to: stealthAddress`;
|
|
52
|
+
* the inner data is what executes once the EOA's code is set to the relayer.
|
|
53
|
+
*/
|
|
54
|
+
function buildEthSweepCalldata(req) {
|
|
55
|
+
return (0, viem_1.encodeFunctionData)({
|
|
56
|
+
abi: SWEEP_ETH_ABI,
|
|
57
|
+
functionName: 'sweepETH',
|
|
58
|
+
args: [req.destination, BigInt(req.deadline), req.signature],
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Submit the type-0x04 tx and wait for receipt.
|
|
63
|
+
*
|
|
64
|
+
* On any viem error we wrap with RelayBroadcastError(name, { cause: err }) so
|
|
65
|
+
* the server can walk the cause chain and log the actual revert reason
|
|
66
|
+
* (public on-chain data) without surfacing it to clients.
|
|
67
|
+
*/
|
|
68
|
+
async function relayEthRequest(ctx, req) {
|
|
69
|
+
const data = buildEthSweepCalldata(req);
|
|
70
|
+
const authorization = reconstructAuthorization(req);
|
|
71
|
+
try {
|
|
72
|
+
const txHash = await ctx.walletClient.sendTransaction({
|
|
73
|
+
account: ctx.account,
|
|
74
|
+
chain: ctx.walletClient.chain ?? null,
|
|
75
|
+
to: req.stealthAddress,
|
|
76
|
+
data,
|
|
77
|
+
value: 0n,
|
|
78
|
+
authorizationList: [authorization],
|
|
79
|
+
});
|
|
80
|
+
const receipt = await ctx.publicClient.waitForTransactionReceipt({
|
|
81
|
+
hash: txHash,
|
|
82
|
+
});
|
|
83
|
+
if (receipt.status !== 'success') {
|
|
84
|
+
throw new errors_js_1.RelayBroadcastError('TransactionReverted');
|
|
85
|
+
}
|
|
86
|
+
return { txHash, blockNumber: receipt.blockNumber };
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
if (err instanceof errors_js_1.RelayBroadcastError)
|
|
90
|
+
throw err;
|
|
91
|
+
const name = err instanceof Error ? err.name : 'UnknownError';
|
|
92
|
+
throw new errors_js_1.RelayBroadcastError(name, { cause: err });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=relay-eth.js.map
|