@metamask-previews/wallet-cli 0.0.0-preview-b3db565c8 → 0.0.0-preview-8bfa290fb
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/CHANGELOG.md +4 -8
- package/dist/commands/daemon/call.cjs +79 -0
- package/dist/commands/daemon/call.cjs.map +1 -0
- package/dist/commands/daemon/{call.d.ts → call.d.cts} +2 -2
- package/dist/commands/daemon/call.d.cts.map +1 -0
- package/dist/commands/daemon/call.d.mts +14 -0
- package/dist/commands/daemon/call.d.mts.map +1 -0
- package/dist/commands/daemon/{call.js → call.mjs} +31 -30
- package/dist/commands/daemon/call.mjs.map +1 -0
- package/dist/commands/daemon/list.cjs +48 -0
- package/dist/commands/daemon/list.cjs.map +1 -0
- package/dist/commands/daemon/{list.d.ts → list.d.cts} +2 -2
- package/dist/commands/daemon/list.d.cts.map +1 -0
- package/dist/commands/daemon/list.d.mts +7 -0
- package/dist/commands/daemon/list.d.mts.map +1 -0
- package/dist/commands/daemon/{list.js → list.mjs} +10 -9
- package/dist/commands/daemon/list.mjs.map +1 -0
- package/dist/commands/daemon/purge.cjs +58 -0
- package/dist/commands/daemon/purge.cjs.map +1 -0
- package/dist/commands/daemon/{purge.d.ts → purge.d.cts} +2 -2
- package/dist/commands/daemon/purge.d.cts.map +1 -0
- package/dist/commands/daemon/purge.d.mts +10 -0
- package/dist/commands/daemon/purge.d.mts.map +1 -0
- package/dist/commands/daemon/{purge.js → purge.mjs} +20 -19
- package/dist/commands/daemon/purge.mjs.map +1 -0
- package/dist/commands/daemon/start.cjs +52 -0
- package/dist/commands/daemon/{start.js.map → start.cjs.map} +1 -1
- package/dist/commands/daemon/{start.d.ts → start.d.cts} +2 -2
- package/dist/commands/daemon/start.d.cts.map +1 -0
- package/dist/commands/daemon/start.d.mts +12 -0
- package/dist/commands/daemon/start.d.mts.map +1 -0
- package/dist/commands/daemon/start.mjs +50 -0
- package/dist/commands/daemon/start.mjs.map +1 -0
- package/dist/commands/daemon/status.cjs +59 -0
- package/dist/commands/daemon/status.cjs.map +1 -0
- package/dist/commands/daemon/{status.d.ts → status.d.cts} +2 -2
- package/dist/commands/daemon/status.d.cts.map +1 -0
- package/dist/commands/daemon/status.d.mts +7 -0
- package/dist/commands/daemon/status.d.mts.map +1 -0
- package/dist/commands/daemon/{status.js → status.mjs} +11 -10
- package/dist/commands/daemon/status.mjs.map +1 -0
- package/dist/commands/daemon/stop.cjs +26 -0
- package/dist/commands/daemon/stop.cjs.map +1 -0
- package/dist/commands/daemon/{stop.d.ts → stop.d.cts} +2 -2
- package/dist/commands/daemon/stop.d.cts.map +1 -0
- package/dist/commands/daemon/stop.d.mts +7 -0
- package/dist/commands/daemon/stop.d.mts.map +1 -0
- package/dist/commands/daemon/{stop.js → stop.mjs} +10 -9
- package/dist/commands/daemon/stop.mjs.map +1 -0
- package/dist/commands/wallet/send.cjs +332 -0
- package/dist/commands/wallet/send.cjs.map +1 -0
- package/dist/commands/wallet/{send.d.ts → send.d.cts} +3 -3
- package/dist/commands/wallet/send.d.cts.map +1 -0
- package/dist/commands/wallet/send.d.mts +42 -0
- package/dist/commands/wallet/send.d.mts.map +1 -0
- package/dist/commands/wallet/{send.js → send.mjs} +123 -111
- package/dist/commands/wallet/send.mjs.map +1 -0
- package/dist/commands/wallet/unlock.cjs +75 -0
- package/dist/commands/wallet/unlock.cjs.map +1 -0
- package/dist/commands/wallet/{unlock.d.ts → unlock.d.cts} +2 -2
- package/dist/commands/wallet/unlock.d.cts.map +1 -0
- package/dist/commands/wallet/unlock.d.mts +11 -0
- package/dist/commands/wallet/unlock.d.mts.map +1 -0
- package/dist/commands/wallet/{unlock.js → unlock.mjs} +30 -29
- package/dist/commands/wallet/unlock.mjs.map +1 -0
- package/dist/daemon/auto-approval.cjs +86 -0
- package/dist/daemon/auto-approval.cjs.map +1 -0
- package/dist/daemon/{auto-approval.d.ts → auto-approval.d.cts} +3 -3
- package/dist/daemon/auto-approval.d.cts.map +1 -0
- package/dist/daemon/auto-approval.d.mts +22 -0
- package/dist/daemon/auto-approval.d.mts.map +1 -0
- package/dist/daemon/{auto-approval.js → auto-approval.mjs} +1 -1
- package/dist/daemon/auto-approval.mjs.map +1 -0
- package/dist/daemon/daemon-client.cjs +149 -0
- package/dist/daemon/daemon-client.cjs.map +1 -0
- package/dist/daemon/{daemon-client.d.ts → daemon-client.d.cts} +2 -2
- package/dist/daemon/daemon-client.d.cts.map +1 -0
- package/dist/daemon/daemon-client.d.mts +84 -0
- package/dist/daemon/daemon-client.d.mts.map +1 -0
- package/dist/daemon/{daemon-client.js → daemon-client.mjs} +8 -8
- package/dist/daemon/daemon-client.mjs.map +1 -0
- package/dist/daemon/daemon-entry.cjs +278 -0
- package/dist/daemon/daemon-entry.cjs.map +1 -0
- package/dist/daemon/daemon-entry.d.cts +2 -0
- package/dist/daemon/daemon-entry.d.cts.map +1 -0
- package/dist/daemon/daemon-entry.d.mts +2 -0
- package/dist/daemon/daemon-entry.d.mts.map +1 -0
- package/dist/daemon/{daemon-entry.js → daemon-entry.mjs} +12 -12
- package/dist/daemon/daemon-entry.mjs.map +1 -0
- package/dist/daemon/daemon-spawn.cjs +138 -0
- package/dist/daemon/daemon-spawn.cjs.map +1 -0
- package/dist/daemon/{daemon-spawn.d.ts → daemon-spawn.d.cts} +2 -2
- package/dist/daemon/daemon-spawn.d.cts.map +1 -0
- package/dist/daemon/daemon-spawn.d.mts +28 -0
- package/dist/daemon/daemon-spawn.d.mts.map +1 -0
- package/dist/daemon/{daemon-spawn.js → daemon-spawn.mjs} +7 -7
- package/dist/daemon/daemon-spawn.mjs.map +1 -0
- package/dist/daemon/data-dir.cjs +23 -0
- package/dist/daemon/data-dir.cjs.map +1 -0
- package/dist/daemon/{data-dir.d.ts → data-dir.d.cts} +1 -1
- package/dist/daemon/data-dir.d.cts.map +1 -0
- package/dist/daemon/data-dir.d.mts +14 -0
- package/dist/daemon/data-dir.d.mts.map +1 -0
- package/dist/daemon/{data-dir.js → data-dir.mjs} +3 -3
- package/dist/daemon/data-dir.mjs.map +1 -0
- package/dist/daemon/paths.cjs +20 -0
- package/dist/daemon/paths.cjs.map +1 -0
- package/dist/daemon/{paths.d.ts → paths.d.cts} +2 -2
- package/dist/daemon/paths.d.cts.map +1 -0
- package/dist/daemon/paths.d.mts +9 -0
- package/dist/daemon/paths.d.mts.map +1 -0
- package/dist/daemon/{paths.js → paths.mjs} +2 -2
- package/dist/daemon/paths.mjs.map +1 -0
- package/dist/daemon/prompts.cjs +55 -0
- package/dist/daemon/prompts.cjs.map +1 -0
- package/dist/daemon/{prompts.d.ts → prompts.d.cts} +1 -1
- package/dist/daemon/prompts.d.cts.map +1 -0
- package/dist/daemon/prompts.d.mts +31 -0
- package/dist/daemon/prompts.d.mts.map +1 -0
- package/dist/daemon/{prompts.js → prompts.mjs} +4 -4
- package/dist/daemon/prompts.mjs.map +1 -0
- package/dist/daemon/rpc-socket-server.cjs +284 -0
- package/dist/daemon/rpc-socket-server.cjs.map +1 -0
- package/dist/daemon/{rpc-socket-server.d.ts → rpc-socket-server.d.cts} +2 -2
- package/dist/daemon/rpc-socket-server.d.cts.map +1 -0
- package/dist/daemon/rpc-socket-server.d.mts +44 -0
- package/dist/daemon/rpc-socket-server.d.mts.map +1 -0
- package/dist/daemon/{rpc-socket-server.js → rpc-socket-server.mjs} +8 -8
- package/dist/daemon/rpc-socket-server.mjs.map +1 -0
- package/dist/daemon/secrets.cjs +137 -0
- package/dist/daemon/secrets.cjs.map +1 -0
- package/dist/daemon/{secrets.d.ts → secrets.d.cts} +1 -1
- package/dist/daemon/secrets.d.cts.map +1 -0
- package/dist/daemon/secrets.d.mts +72 -0
- package/dist/daemon/secrets.d.mts.map +1 -0
- package/dist/daemon/{secrets.js → secrets.mjs} +23 -11
- package/dist/daemon/secrets.mjs.map +1 -0
- package/dist/daemon/send-transaction.cjs +147 -0
- package/dist/daemon/send-transaction.cjs.map +1 -0
- package/dist/daemon/{send-transaction.d.ts → send-transaction.d.cts} +9 -9
- package/dist/daemon/send-transaction.d.cts.map +1 -0
- package/dist/daemon/send-transaction.d.mts +102 -0
- package/dist/daemon/send-transaction.d.mts.map +1 -0
- package/dist/daemon/{send-transaction.js → send-transaction.mjs} +3 -3
- package/dist/daemon/send-transaction.mjs.map +1 -0
- package/dist/daemon/socket-line.cjs +93 -0
- package/dist/daemon/socket-line.cjs.map +1 -0
- package/dist/daemon/{socket-line.d.ts → socket-line.d.cts} +5 -2
- package/dist/daemon/socket-line.d.cts.map +1 -0
- package/dist/daemon/socket-line.d.mts +21 -0
- package/dist/daemon/socket-line.d.mts.map +1 -0
- package/dist/daemon/{socket-line.js → socket-line.mjs} +1 -1
- package/dist/daemon/socket-line.mjs.map +1 -0
- package/dist/daemon/stop-daemon.cjs +117 -0
- package/dist/daemon/stop-daemon.cjs.map +1 -0
- package/dist/daemon/{stop-daemon.d.ts → stop-daemon.d.cts} +1 -1
- package/dist/daemon/stop-daemon.d.cts.map +1 -0
- package/dist/daemon/stop-daemon.d.mts +28 -0
- package/dist/daemon/stop-daemon.d.mts.map +1 -0
- package/dist/daemon/{stop-daemon.js → stop-daemon.mjs} +7 -7
- package/dist/daemon/stop-daemon.mjs.map +1 -0
- package/dist/daemon/types.cjs +20 -0
- package/dist/daemon/types.cjs.map +1 -0
- package/dist/daemon/{types.d.ts → types.d.cts} +4 -4
- package/dist/daemon/types.d.cts.map +1 -0
- package/dist/daemon/types.d.mts +94 -0
- package/dist/daemon/types.d.mts.map +1 -0
- package/dist/daemon/{types.js → types.mjs} +1 -1
- package/dist/daemon/types.mjs.map +1 -0
- package/dist/daemon/utils.cjs +172 -0
- package/dist/daemon/utils.cjs.map +1 -0
- package/dist/daemon/{utils.d.ts → utils.d.cts} +3 -2
- package/dist/daemon/utils.d.cts.map +1 -0
- package/dist/daemon/utils.d.mts +87 -0
- package/dist/daemon/utils.d.mts.map +1 -0
- package/dist/daemon/{utils.js → utils.mjs} +3 -3
- package/dist/daemon/utils.mjs.map +1 -0
- package/dist/daemon/wallet-factory.cjs +341 -0
- package/dist/daemon/wallet-factory.cjs.map +1 -0
- package/dist/daemon/{wallet-factory.d.ts → wallet-factory.d.cts} +4 -4
- package/dist/daemon/wallet-factory.d.cts.map +1 -0
- package/dist/daemon/wallet-factory.d.mts +64 -0
- package/dist/daemon/wallet-factory.d.mts.map +1 -0
- package/dist/daemon/{wallet-factory.js → wallet-factory.mjs} +12 -12
- package/dist/daemon/wallet-factory.mjs.map +1 -0
- package/dist/persistence/KeyValueStore.cjs +79 -0
- package/dist/persistence/KeyValueStore.cjs.map +1 -0
- package/dist/persistence/{KeyValueStore.d.ts → KeyValueStore.d.cts} +2 -2
- package/dist/persistence/KeyValueStore.d.cts.map +1 -0
- package/dist/persistence/KeyValueStore.d.mts +18 -0
- package/dist/persistence/KeyValueStore.d.mts.map +1 -0
- package/dist/persistence/KeyValueStore.mjs +79 -0
- package/dist/persistence/KeyValueStore.mjs.map +1 -0
- package/dist/persistence/persistence.cjs +204 -0
- package/dist/persistence/persistence.cjs.map +1 -0
- package/dist/persistence/{persistence.d.ts → persistence.d.cts} +5 -5
- package/dist/persistence/persistence.d.cts.map +1 -0
- package/dist/persistence/persistence.d.mts +45 -0
- package/dist/persistence/persistence.d.mts.map +1 -0
- package/dist/persistence/{persistence.js → persistence.mjs} +2 -2
- package/dist/persistence/persistence.mjs.map +1 -0
- package/dist/test/run-command.cjs +73 -0
- package/dist/test/run-command.cjs.map +1 -0
- package/dist/test/{run-command.d.ts → run-command.d.cts} +4 -4
- package/dist/test/run-command.d.cts.map +1 -0
- package/dist/test/run-command.d.mts +27 -0
- package/dist/test/run-command.d.mts.map +1 -0
- package/dist/test/{run-command.js → run-command.mjs} +3 -3
- package/dist/test/run-command.mjs.map +1 -0
- package/package.json +15 -19
- package/dist/commands/daemon/call.d.ts.map +0 -1
- package/dist/commands/daemon/call.js.map +0 -1
- package/dist/commands/daemon/list.d.ts.map +0 -1
- package/dist/commands/daemon/list.js.map +0 -1
- package/dist/commands/daemon/purge.d.ts.map +0 -1
- package/dist/commands/daemon/purge.js.map +0 -1
- package/dist/commands/daemon/start.d.ts.map +0 -1
- package/dist/commands/daemon/start.js +0 -49
- package/dist/commands/daemon/status.d.ts.map +0 -1
- package/dist/commands/daemon/status.js.map +0 -1
- package/dist/commands/daemon/stop.d.ts.map +0 -1
- package/dist/commands/daemon/stop.js.map +0 -1
- package/dist/commands/wallet/send.d.ts.map +0 -1
- package/dist/commands/wallet/send.js.map +0 -1
- package/dist/commands/wallet/unlock.d.ts.map +0 -1
- package/dist/commands/wallet/unlock.js.map +0 -1
- package/dist/daemon/auto-approval.d.ts.map +0 -1
- package/dist/daemon/auto-approval.js.map +0 -1
- package/dist/daemon/daemon-client.d.ts.map +0 -1
- package/dist/daemon/daemon-client.js.map +0 -1
- package/dist/daemon/daemon-entry.d.ts +0 -2
- package/dist/daemon/daemon-entry.d.ts.map +0 -1
- package/dist/daemon/daemon-entry.js.map +0 -1
- package/dist/daemon/daemon-spawn.d.ts.map +0 -1
- package/dist/daemon/daemon-spawn.js.map +0 -1
- package/dist/daemon/data-dir.d.ts.map +0 -1
- package/dist/daemon/data-dir.js.map +0 -1
- package/dist/daemon/paths.d.ts.map +0 -1
- package/dist/daemon/paths.js.map +0 -1
- package/dist/daemon/prompts.d.ts.map +0 -1
- package/dist/daemon/prompts.js.map +0 -1
- package/dist/daemon/rpc-socket-server.d.ts.map +0 -1
- package/dist/daemon/rpc-socket-server.js.map +0 -1
- package/dist/daemon/secrets.d.ts.map +0 -1
- package/dist/daemon/secrets.js.map +0 -1
- package/dist/daemon/send-transaction.d.ts.map +0 -1
- package/dist/daemon/send-transaction.js.map +0 -1
- package/dist/daemon/socket-line.d.ts.map +0 -1
- package/dist/daemon/socket-line.js.map +0 -1
- package/dist/daemon/stop-daemon.d.ts.map +0 -1
- package/dist/daemon/stop-daemon.js.map +0 -1
- package/dist/daemon/types.d.ts.map +0 -1
- package/dist/daemon/types.js.map +0 -1
- package/dist/daemon/utils.d.ts.map +0 -1
- package/dist/daemon/utils.js.map +0 -1
- package/dist/daemon/wallet-factory.d.ts.map +0 -1
- package/dist/daemon/wallet-factory.js.map +0 -1
- package/dist/persistence/KeyValueStore.d.ts.map +0 -1
- package/dist/persistence/KeyValueStore.js +0 -59
- package/dist/persistence/KeyValueStore.js.map +0 -1
- package/dist/persistence/persistence.d.ts.map +0 -1
- package/dist/persistence/persistence.js.map +0 -1
- package/dist/test/run-command.d.ts.map +0 -1
- package/dist/test/run-command.js.map +0 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runSendTransaction = exports.SendTransactionBroadcastResultStruct = exports.SendTransactionDryRunResultStruct = exports.SendTransactionParamsStruct = void 0;
|
|
4
|
+
const superstruct_1 = require("@metamask/superstruct");
|
|
5
|
+
const utils_1 = require("@metamask/utils");
|
|
6
|
+
/**
|
|
7
|
+
* Origin recorded on daemon-initiated transactions. Mirrors
|
|
8
|
+
* `ORIGIN_METAMASK` from `@metamask/controller-utils` (a single string
|
|
9
|
+
* constant, inlined here to avoid pulling that whole package in as a
|
|
10
|
+
* dependency for one value).
|
|
11
|
+
*/
|
|
12
|
+
const INTERNAL_ORIGIN = 'metamask';
|
|
13
|
+
/**
|
|
14
|
+
* Struct for a `0x`-prefixed 20-byte hex address. `isValidHexAddress` accepts
|
|
15
|
+
* an all-lowercase address or a valid EIP-55 checksummed one, so a plain
|
|
16
|
+
* lowercase paste works while a mistyped mixed-case address is rejected.
|
|
17
|
+
*/
|
|
18
|
+
const AddressStruct = (0, superstruct_1.define)('Address', (value) => typeof value === 'string' && (0, utils_1.isValidHexAddress)(value)
|
|
19
|
+
? true
|
|
20
|
+
: 'Expected a 0x-prefixed 20-byte hex address');
|
|
21
|
+
/**
|
|
22
|
+
* Params struct for the `sendTransaction` RPC method.
|
|
23
|
+
*
|
|
24
|
+
* The transaction fields (`to`, `value`, `data`, gas overrides) are canonical
|
|
25
|
+
* `0x`-prefixed hex — any unit conversion (e.g. ether → wei) is the CLI
|
|
26
|
+
* command's job, so the daemon boundary stays unambiguous. Exactly one of
|
|
27
|
+
* `networkClientId` / `chainId` selects the network client; the refinement
|
|
28
|
+
* below rejects supplying both or neither. `dryRun` short-circuits before
|
|
29
|
+
* broadcast (see {@link runSendTransaction}).
|
|
30
|
+
*/
|
|
31
|
+
exports.SendTransactionParamsStruct = (0, superstruct_1.refine)((0, superstruct_1.object)({
|
|
32
|
+
to: AddressStruct,
|
|
33
|
+
from: (0, superstruct_1.optional)(AddressStruct),
|
|
34
|
+
value: (0, superstruct_1.optional)(utils_1.StrictHexStruct),
|
|
35
|
+
data: (0, superstruct_1.optional)(utils_1.StrictHexStruct),
|
|
36
|
+
gas: (0, superstruct_1.optional)(utils_1.StrictHexStruct),
|
|
37
|
+
maxFeePerGas: (0, superstruct_1.optional)(utils_1.StrictHexStruct),
|
|
38
|
+
maxPriorityFeePerGas: (0, superstruct_1.optional)(utils_1.StrictHexStruct),
|
|
39
|
+
gasPrice: (0, superstruct_1.optional)(utils_1.StrictHexStruct),
|
|
40
|
+
networkClientId: (0, superstruct_1.optional)((0, superstruct_1.nonempty)((0, superstruct_1.string)())),
|
|
41
|
+
chainId: (0, superstruct_1.optional)(utils_1.StrictHexStruct),
|
|
42
|
+
dryRun: (0, superstruct_1.optional)((0, superstruct_1.boolean)()),
|
|
43
|
+
}), 'SendTransactionParams', (value) => {
|
|
44
|
+
if ((value.networkClientId === undefined) ===
|
|
45
|
+
(value.chainId === undefined)) {
|
|
46
|
+
return 'Exactly one of `networkClientId` or `chainId` must be provided';
|
|
47
|
+
}
|
|
48
|
+
// `gasPrice` (legacy) and the EIP-1559 fields are mutually exclusive:
|
|
49
|
+
// `TransactionController` rejects the combination at broadcast, so reject
|
|
50
|
+
// it here at the boundary instead — otherwise a dry-run and the
|
|
51
|
+
// confirmation preview would show a plan that always fails at send.
|
|
52
|
+
if (value.gasPrice !== undefined &&
|
|
53
|
+
(value.maxFeePerGas !== undefined ||
|
|
54
|
+
value.maxPriorityFeePerGas !== undefined)) {
|
|
55
|
+
return '`gasPrice` cannot be combined with `maxFeePerGas` or `maxPriorityFeePerGas`';
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* The resolved plan a `dryRun` returns: the network client and sender the
|
|
61
|
+
* daemon would use, without adding or broadcasting anything. Exported as a
|
|
62
|
+
* struct so the CLI can validate the payload it reads back over JSON-RPC
|
|
63
|
+
* (which erases types on the wire) rather than trusting its shape.
|
|
64
|
+
*/
|
|
65
|
+
exports.SendTransactionDryRunResultStruct = (0, superstruct_1.object)({
|
|
66
|
+
dryRun: (0, superstruct_1.literal)(true),
|
|
67
|
+
from: utils_1.StrictHexStruct,
|
|
68
|
+
to: utils_1.StrictHexStruct,
|
|
69
|
+
value: utils_1.StrictHexStruct,
|
|
70
|
+
networkClientId: (0, superstruct_1.string)(),
|
|
71
|
+
});
|
|
72
|
+
/**
|
|
73
|
+
* The outcome of a broadcast send: the on-chain hash plus the id/status the
|
|
74
|
+
* daemon tracks the transaction under. Exported as a struct for the same
|
|
75
|
+
* client-side validation reason as {@link SendTransactionDryRunResultStruct}.
|
|
76
|
+
*/
|
|
77
|
+
exports.SendTransactionBroadcastResultStruct = (0, superstruct_1.object)({
|
|
78
|
+
transactionHash: (0, superstruct_1.string)(),
|
|
79
|
+
transactionId: (0, superstruct_1.string)(),
|
|
80
|
+
status: (0, superstruct_1.string)(),
|
|
81
|
+
});
|
|
82
|
+
/**
|
|
83
|
+
* Add and broadcast a transaction through the daemon-hosted
|
|
84
|
+
* `TransactionController`, returning a serializable result.
|
|
85
|
+
*
|
|
86
|
+
* `TransactionController:addTransaction` returns `{ transactionMeta, result }`
|
|
87
|
+
* where `result` is a `Promise<hash>` that resolves once the transaction is
|
|
88
|
+
* signed and broadcast. That promise is not JSON-serializable, so it cannot
|
|
89
|
+
* travel back over the daemon's JSON-RPC socket via the generic `call`
|
|
90
|
+
* dispatch — hence this dedicated handler, which awaits the broadcast
|
|
91
|
+
* server-side and returns only the resolved hash (and id/status).
|
|
92
|
+
*
|
|
93
|
+
* The network client is resolved from `chainId` (via `NetworkController`) when
|
|
94
|
+
* `networkClientId` is not given directly, and `from` defaults to the selected
|
|
95
|
+
* account. The transaction is submitted as `isInternal` (the daemon is driven
|
|
96
|
+
* only by its owner over the `0600` same-user socket), which skips
|
|
97
|
+
* origin/permitted-account validation; its approval request is auto-accepted
|
|
98
|
+
* by the daemon's auto-approval subscription.
|
|
99
|
+
*
|
|
100
|
+
* When `dryRun` is set, the network client and sender are resolved and the
|
|
101
|
+
* params validated, but nothing is added or broadcast — the resolved plan is
|
|
102
|
+
* returned so the CLI can preview it before the user confirms.
|
|
103
|
+
*
|
|
104
|
+
* @param messenger - The wallet root messenger.
|
|
105
|
+
* @param params - Validated `sendTransaction` params.
|
|
106
|
+
* @returns The dry-run plan, or the broadcast hash with its id and status.
|
|
107
|
+
*/
|
|
108
|
+
async function runSendTransaction(messenger, params) {
|
|
109
|
+
const { to, value = '0x0', data, gas, maxFeePerGas, maxPriorityFeePerGas, gasPrice, networkClientId: networkClientIdParam, chainId, dryRun, } = params;
|
|
110
|
+
// The struct guarantees exactly one of `networkClientId` / `chainId`, so
|
|
111
|
+
// `chainId` is defined whenever `networkClientId` is not.
|
|
112
|
+
const networkClientId = networkClientIdParam ??
|
|
113
|
+
messenger.call('NetworkController:findNetworkClientIdByChainId', chainId);
|
|
114
|
+
const from = params.from ??
|
|
115
|
+
messenger.call('AccountsController:getSelectedAccount').address;
|
|
116
|
+
if (dryRun) {
|
|
117
|
+
return { dryRun: true, from, to, value, networkClientId };
|
|
118
|
+
}
|
|
119
|
+
const txParams = {
|
|
120
|
+
from,
|
|
121
|
+
to,
|
|
122
|
+
value,
|
|
123
|
+
...(data === undefined ? {} : { data }),
|
|
124
|
+
...(gas === undefined ? {} : { gas }),
|
|
125
|
+
...(maxFeePerGas === undefined ? {} : { maxFeePerGas }),
|
|
126
|
+
...(maxPriorityFeePerGas === undefined ? {} : { maxPriorityFeePerGas }),
|
|
127
|
+
...(gasPrice === undefined ? {} : { gasPrice }),
|
|
128
|
+
};
|
|
129
|
+
const { result, transactionMeta } = await messenger.call('TransactionController:addTransaction', txParams, { networkClientId, origin: INTERNAL_ORIGIN, isInternal: true });
|
|
130
|
+
// `result` resolves only once the transaction is signed and broadcast (see
|
|
131
|
+
// the function JSDoc); awaiting it here is what this handler exists for.
|
|
132
|
+
const transactionHash = await result;
|
|
133
|
+
// Re-read the live record for the current status: `transactionMeta` is the
|
|
134
|
+
// creation snapshot (still `unapproved`). If the record is already gone,
|
|
135
|
+
// fall back to `submitted` — reaching this point means `result` resolved, so
|
|
136
|
+
// the transaction was broadcast, never merely `unapproved`.
|
|
137
|
+
const [current] = messenger.call('TransactionController:getTransactions', {
|
|
138
|
+
searchCriteria: { id: transactionMeta.id },
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
transactionHash,
|
|
142
|
+
transactionId: transactionMeta.id,
|
|
143
|
+
status: current?.status ?? 'submitted',
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
exports.runSendTransaction = runSendTransaction;
|
|
147
|
+
//# sourceMappingURL=send-transaction.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-transaction.cjs","sourceRoot":"","sources":["../../src/daemon/send-transaction.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n boolean,\n define,\n literal,\n nonempty,\n object,\n optional,\n refine,\n string,\n} from '@metamask/superstruct';\nimport type { Infer } from '@metamask/superstruct';\nimport { isValidHexAddress, StrictHexStruct } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\nimport type {\n DefaultActions,\n DefaultEvents,\n RootMessenger,\n} from '@metamask/wallet';\n\n/**\n * Origin recorded on daemon-initiated transactions. Mirrors\n * `ORIGIN_METAMASK` from `@metamask/controller-utils` (a single string\n * constant, inlined here to avoid pulling that whole package in as a\n * dependency for one value).\n */\nconst INTERNAL_ORIGIN = 'metamask';\n\n/**\n * Struct for a `0x`-prefixed 20-byte hex address. `isValidHexAddress` accepts\n * an all-lowercase address or a valid EIP-55 checksummed one, so a plain\n * lowercase paste works while a mistyped mixed-case address is rejected.\n */\nconst AddressStruct = define<Hex>('Address', (value) =>\n typeof value === 'string' && isValidHexAddress(value as Hex)\n ? true\n : 'Expected a 0x-prefixed 20-byte hex address',\n);\n\n/**\n * Params struct for the `sendTransaction` RPC method.\n *\n * The transaction fields (`to`, `value`, `data`, gas overrides) are canonical\n * `0x`-prefixed hex — any unit conversion (e.g. ether → wei) is the CLI\n * command's job, so the daemon boundary stays unambiguous. Exactly one of\n * `networkClientId` / `chainId` selects the network client; the refinement\n * below rejects supplying both or neither. `dryRun` short-circuits before\n * broadcast (see {@link runSendTransaction}).\n */\nexport const SendTransactionParamsStruct = refine(\n object({\n to: AddressStruct,\n from: optional(AddressStruct),\n value: optional(StrictHexStruct),\n data: optional(StrictHexStruct),\n gas: optional(StrictHexStruct),\n maxFeePerGas: optional(StrictHexStruct),\n maxPriorityFeePerGas: optional(StrictHexStruct),\n gasPrice: optional(StrictHexStruct),\n networkClientId: optional(nonempty(string())),\n chainId: optional(StrictHexStruct),\n dryRun: optional(boolean()),\n }),\n 'SendTransactionParams',\n (value) => {\n if (\n (value.networkClientId === undefined) ===\n (value.chainId === undefined)\n ) {\n return 'Exactly one of `networkClientId` or `chainId` must be provided';\n }\n // `gasPrice` (legacy) and the EIP-1559 fields are mutually exclusive:\n // `TransactionController` rejects the combination at broadcast, so reject\n // it here at the boundary instead — otherwise a dry-run and the\n // confirmation preview would show a plan that always fails at send.\n if (\n value.gasPrice !== undefined &&\n (value.maxFeePerGas !== undefined ||\n value.maxPriorityFeePerGas !== undefined)\n ) {\n return '`gasPrice` cannot be combined with `maxFeePerGas` or `maxPriorityFeePerGas`';\n }\n return true;\n },\n);\n\nexport type SendTransactionParams = Infer<typeof SendTransactionParamsStruct>;\n\n/**\n * The resolved plan a `dryRun` returns: the network client and sender the\n * daemon would use, without adding or broadcasting anything. Exported as a\n * struct so the CLI can validate the payload it reads back over JSON-RPC\n * (which erases types on the wire) rather than trusting its shape.\n */\nexport const SendTransactionDryRunResultStruct = object({\n dryRun: literal(true),\n from: StrictHexStruct,\n to: StrictHexStruct,\n value: StrictHexStruct,\n networkClientId: string(),\n});\n\n/**\n * The outcome of a broadcast send: the on-chain hash plus the id/status the\n * daemon tracks the transaction under. Exported as a struct for the same\n * client-side validation reason as {@link SendTransactionDryRunResultStruct}.\n */\nexport const SendTransactionBroadcastResultStruct = object({\n transactionHash: string(),\n transactionId: string(),\n status: string(),\n});\n\nexport type SendTransactionDryRunResult = Infer<\n typeof SendTransactionDryRunResultStruct\n>;\n\nexport type SendTransactionBroadcastResult = Infer<\n typeof SendTransactionBroadcastResultStruct\n>;\n\nexport type SendTransactionResult =\n | SendTransactionDryRunResult\n | SendTransactionBroadcastResult;\n\n/**\n * Add and broadcast a transaction through the daemon-hosted\n * `TransactionController`, returning a serializable result.\n *\n * `TransactionController:addTransaction` returns `{ transactionMeta, result }`\n * where `result` is a `Promise<hash>` that resolves once the transaction is\n * signed and broadcast. That promise is not JSON-serializable, so it cannot\n * travel back over the daemon's JSON-RPC socket via the generic `call`\n * dispatch — hence this dedicated handler, which awaits the broadcast\n * server-side and returns only the resolved hash (and id/status).\n *\n * The network client is resolved from `chainId` (via `NetworkController`) when\n * `networkClientId` is not given directly, and `from` defaults to the selected\n * account. The transaction is submitted as `isInternal` (the daemon is driven\n * only by its owner over the `0600` same-user socket), which skips\n * origin/permitted-account validation; its approval request is auto-accepted\n * by the daemon's auto-approval subscription.\n *\n * When `dryRun` is set, the network client and sender are resolved and the\n * params validated, but nothing is added or broadcast — the resolved plan is\n * returned so the CLI can preview it before the user confirms.\n *\n * @param messenger - The wallet root messenger.\n * @param params - Validated `sendTransaction` params.\n * @returns The dry-run plan, or the broadcast hash with its id and status.\n */\nexport async function runSendTransaction(\n messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>,\n params: SendTransactionParams,\n): Promise<SendTransactionResult> {\n const {\n to,\n value = '0x0',\n data,\n gas,\n maxFeePerGas,\n maxPriorityFeePerGas,\n gasPrice,\n networkClientId: networkClientIdParam,\n chainId,\n dryRun,\n } = params;\n\n // The struct guarantees exactly one of `networkClientId` / `chainId`, so\n // `chainId` is defined whenever `networkClientId` is not.\n const networkClientId =\n networkClientIdParam ??\n messenger.call(\n 'NetworkController:findNetworkClientIdByChainId',\n chainId as Hex,\n );\n\n const from =\n params.from ??\n (messenger.call('AccountsController:getSelectedAccount').address as Hex);\n\n if (dryRun) {\n return { dryRun: true, from, to, value, networkClientId };\n }\n\n const txParams = {\n from,\n to,\n value,\n ...(data === undefined ? {} : { data }),\n ...(gas === undefined ? {} : { gas }),\n ...(maxFeePerGas === undefined ? {} : { maxFeePerGas }),\n ...(maxPriorityFeePerGas === undefined ? {} : { maxPriorityFeePerGas }),\n ...(gasPrice === undefined ? {} : { gasPrice }),\n };\n\n const { result, transactionMeta } = await messenger.call(\n 'TransactionController:addTransaction',\n txParams,\n { networkClientId, origin: INTERNAL_ORIGIN, isInternal: true },\n );\n\n // `result` resolves only once the transaction is signed and broadcast (see\n // the function JSDoc); awaiting it here is what this handler exists for.\n const transactionHash = await result;\n\n // Re-read the live record for the current status: `transactionMeta` is the\n // creation snapshot (still `unapproved`). If the record is already gone,\n // fall back to `submitted` — reaching this point means `result` resolved, so\n // the transaction was broadcast, never merely `unapproved`.\n const [current] = messenger.call('TransactionController:getTransactions', {\n searchCriteria: { id: transactionMeta.id },\n });\n\n return {\n transactionHash,\n transactionId: transactionMeta.id,\n status: current?.status ?? 'submitted',\n };\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Infer } from
|
|
2
|
-
import type { DefaultActions, DefaultEvents, RootMessenger } from
|
|
1
|
+
import type { Infer } from "@metamask/superstruct";
|
|
2
|
+
import type { DefaultActions, DefaultEvents, RootMessenger } from "@metamask/wallet";
|
|
3
3
|
/**
|
|
4
4
|
* Params struct for the `sendTransaction` RPC method.
|
|
5
5
|
*
|
|
@@ -12,9 +12,9 @@ import type { DefaultActions, DefaultEvents, RootMessenger } from '@metamask/wal
|
|
|
12
12
|
*/
|
|
13
13
|
export declare const SendTransactionParamsStruct: import("@metamask/superstruct").Struct<{
|
|
14
14
|
to: `0x${string}`;
|
|
15
|
-
from?: `0x${string}` | undefined;
|
|
16
|
-
value?: `0x${string}` | undefined;
|
|
17
15
|
data?: `0x${string}` | undefined;
|
|
16
|
+
value?: `0x${string}` | undefined;
|
|
17
|
+
from?: `0x${string}` | undefined;
|
|
18
18
|
gas?: `0x${string}` | undefined;
|
|
19
19
|
maxFeePerGas?: `0x${string}` | undefined;
|
|
20
20
|
maxPriorityFeePerGas?: `0x${string}` | undefined;
|
|
@@ -43,11 +43,11 @@ export type SendTransactionParams = Infer<typeof SendTransactionParamsStruct>;
|
|
|
43
43
|
* (which erases types on the wire) rather than trusting its shape.
|
|
44
44
|
*/
|
|
45
45
|
export declare const SendTransactionDryRunResultStruct: import("@metamask/superstruct").Struct<{
|
|
46
|
-
dryRun: true;
|
|
47
|
-
from: `0x${string}`;
|
|
48
|
-
to: `0x${string}`;
|
|
49
46
|
value: `0x${string}`;
|
|
47
|
+
to: `0x${string}`;
|
|
48
|
+
from: `0x${string}`;
|
|
50
49
|
networkClientId: string;
|
|
50
|
+
dryRun: true;
|
|
51
51
|
}, {
|
|
52
52
|
dryRun: import("@metamask/superstruct").Struct<true, true>;
|
|
53
53
|
from: import("@metamask/superstruct").Struct<`0x${string}`, null>;
|
|
@@ -61,9 +61,9 @@ export declare const SendTransactionDryRunResultStruct: import("@metamask/supers
|
|
|
61
61
|
* client-side validation reason as {@link SendTransactionDryRunResultStruct}.
|
|
62
62
|
*/
|
|
63
63
|
export declare const SendTransactionBroadcastResultStruct: import("@metamask/superstruct").Struct<{
|
|
64
|
+
status: string;
|
|
64
65
|
transactionHash: string;
|
|
65
66
|
transactionId: string;
|
|
66
|
-
status: string;
|
|
67
67
|
}, {
|
|
68
68
|
transactionHash: import("@metamask/superstruct").Struct<string, null>;
|
|
69
69
|
transactionId: import("@metamask/superstruct").Struct<string, null>;
|
|
@@ -99,4 +99,4 @@ export type SendTransactionResult = SendTransactionDryRunResult | SendTransactio
|
|
|
99
99
|
* @returns The dry-run plan, or the broadcast hash with its id and status.
|
|
100
100
|
*/
|
|
101
101
|
export declare function runSendTransaction(messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>, params: SendTransactionParams): Promise<SendTransactionResult>;
|
|
102
|
-
//# sourceMappingURL=send-transaction.d.
|
|
102
|
+
//# sourceMappingURL=send-transaction.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-transaction.d.cts","sourceRoot":"","sources":["../../src/daemon/send-transaction.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,KAAK,EAAE,8BAA8B;AAGnD,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACd,yBAAyB;AAqB1B;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;EAmCvC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAE9E;;;;;GAKG;AACH,eAAO,MAAM,iCAAiC;;;;;;;;;;;;EAM5C,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,oCAAoC;;;;;;;;EAI/C,CAAC;AAEH,MAAM,MAAM,2BAA2B,GAAG,KAAK,CAC7C,OAAO,iCAAiC,CACzC,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG,KAAK,CAChD,OAAO,oCAAoC,CAC5C,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAC7B,2BAA2B,GAC3B,8BAA8B,CAAC;AAEnC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,QAAQ,CAAC,aAAa,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC,EACjE,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CAiEhC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Infer } from "@metamask/superstruct";
|
|
2
|
+
import type { DefaultActions, DefaultEvents, RootMessenger } from "@metamask/wallet";
|
|
3
|
+
/**
|
|
4
|
+
* Params struct for the `sendTransaction` RPC method.
|
|
5
|
+
*
|
|
6
|
+
* The transaction fields (`to`, `value`, `data`, gas overrides) are canonical
|
|
7
|
+
* `0x`-prefixed hex — any unit conversion (e.g. ether → wei) is the CLI
|
|
8
|
+
* command's job, so the daemon boundary stays unambiguous. Exactly one of
|
|
9
|
+
* `networkClientId` / `chainId` selects the network client; the refinement
|
|
10
|
+
* below rejects supplying both or neither. `dryRun` short-circuits before
|
|
11
|
+
* broadcast (see {@link runSendTransaction}).
|
|
12
|
+
*/
|
|
13
|
+
export declare const SendTransactionParamsStruct: import("@metamask/superstruct").Struct<{
|
|
14
|
+
to: `0x${string}`;
|
|
15
|
+
data?: `0x${string}` | undefined;
|
|
16
|
+
value?: `0x${string}` | undefined;
|
|
17
|
+
from?: `0x${string}` | undefined;
|
|
18
|
+
gas?: `0x${string}` | undefined;
|
|
19
|
+
maxFeePerGas?: `0x${string}` | undefined;
|
|
20
|
+
maxPriorityFeePerGas?: `0x${string}` | undefined;
|
|
21
|
+
gasPrice?: `0x${string}` | undefined;
|
|
22
|
+
networkClientId?: string | undefined;
|
|
23
|
+
chainId?: `0x${string}` | undefined;
|
|
24
|
+
dryRun?: boolean | undefined;
|
|
25
|
+
}, {
|
|
26
|
+
to: import("@metamask/superstruct").Struct<`0x${string}`, null>;
|
|
27
|
+
from: import("@metamask/superstruct").Struct<`0x${string}` | undefined, null>;
|
|
28
|
+
value: import("@metamask/superstruct").Struct<`0x${string}` | undefined, null>;
|
|
29
|
+
data: import("@metamask/superstruct").Struct<`0x${string}` | undefined, null>;
|
|
30
|
+
gas: import("@metamask/superstruct").Struct<`0x${string}` | undefined, null>;
|
|
31
|
+
maxFeePerGas: import("@metamask/superstruct").Struct<`0x${string}` | undefined, null>;
|
|
32
|
+
maxPriorityFeePerGas: import("@metamask/superstruct").Struct<`0x${string}` | undefined, null>;
|
|
33
|
+
gasPrice: import("@metamask/superstruct").Struct<`0x${string}` | undefined, null>;
|
|
34
|
+
networkClientId: import("@metamask/superstruct").Struct<string | undefined, null>;
|
|
35
|
+
chainId: import("@metamask/superstruct").Struct<`0x${string}` | undefined, null>;
|
|
36
|
+
dryRun: import("@metamask/superstruct").Struct<boolean | undefined, null>;
|
|
37
|
+
}>;
|
|
38
|
+
export type SendTransactionParams = Infer<typeof SendTransactionParamsStruct>;
|
|
39
|
+
/**
|
|
40
|
+
* The resolved plan a `dryRun` returns: the network client and sender the
|
|
41
|
+
* daemon would use, without adding or broadcasting anything. Exported as a
|
|
42
|
+
* struct so the CLI can validate the payload it reads back over JSON-RPC
|
|
43
|
+
* (which erases types on the wire) rather than trusting its shape.
|
|
44
|
+
*/
|
|
45
|
+
export declare const SendTransactionDryRunResultStruct: import("@metamask/superstruct").Struct<{
|
|
46
|
+
value: `0x${string}`;
|
|
47
|
+
to: `0x${string}`;
|
|
48
|
+
from: `0x${string}`;
|
|
49
|
+
networkClientId: string;
|
|
50
|
+
dryRun: true;
|
|
51
|
+
}, {
|
|
52
|
+
dryRun: import("@metamask/superstruct").Struct<true, true>;
|
|
53
|
+
from: import("@metamask/superstruct").Struct<`0x${string}`, null>;
|
|
54
|
+
to: import("@metamask/superstruct").Struct<`0x${string}`, null>;
|
|
55
|
+
value: import("@metamask/superstruct").Struct<`0x${string}`, null>;
|
|
56
|
+
networkClientId: import("@metamask/superstruct").Struct<string, null>;
|
|
57
|
+
}>;
|
|
58
|
+
/**
|
|
59
|
+
* The outcome of a broadcast send: the on-chain hash plus the id/status the
|
|
60
|
+
* daemon tracks the transaction under. Exported as a struct for the same
|
|
61
|
+
* client-side validation reason as {@link SendTransactionDryRunResultStruct}.
|
|
62
|
+
*/
|
|
63
|
+
export declare const SendTransactionBroadcastResultStruct: import("@metamask/superstruct").Struct<{
|
|
64
|
+
status: string;
|
|
65
|
+
transactionHash: string;
|
|
66
|
+
transactionId: string;
|
|
67
|
+
}, {
|
|
68
|
+
transactionHash: import("@metamask/superstruct").Struct<string, null>;
|
|
69
|
+
transactionId: import("@metamask/superstruct").Struct<string, null>;
|
|
70
|
+
status: import("@metamask/superstruct").Struct<string, null>;
|
|
71
|
+
}>;
|
|
72
|
+
export type SendTransactionDryRunResult = Infer<typeof SendTransactionDryRunResultStruct>;
|
|
73
|
+
export type SendTransactionBroadcastResult = Infer<typeof SendTransactionBroadcastResultStruct>;
|
|
74
|
+
export type SendTransactionResult = SendTransactionDryRunResult | SendTransactionBroadcastResult;
|
|
75
|
+
/**
|
|
76
|
+
* Add and broadcast a transaction through the daemon-hosted
|
|
77
|
+
* `TransactionController`, returning a serializable result.
|
|
78
|
+
*
|
|
79
|
+
* `TransactionController:addTransaction` returns `{ transactionMeta, result }`
|
|
80
|
+
* where `result` is a `Promise<hash>` that resolves once the transaction is
|
|
81
|
+
* signed and broadcast. That promise is not JSON-serializable, so it cannot
|
|
82
|
+
* travel back over the daemon's JSON-RPC socket via the generic `call`
|
|
83
|
+
* dispatch — hence this dedicated handler, which awaits the broadcast
|
|
84
|
+
* server-side and returns only the resolved hash (and id/status).
|
|
85
|
+
*
|
|
86
|
+
* The network client is resolved from `chainId` (via `NetworkController`) when
|
|
87
|
+
* `networkClientId` is not given directly, and `from` defaults to the selected
|
|
88
|
+
* account. The transaction is submitted as `isInternal` (the daemon is driven
|
|
89
|
+
* only by its owner over the `0600` same-user socket), which skips
|
|
90
|
+
* origin/permitted-account validation; its approval request is auto-accepted
|
|
91
|
+
* by the daemon's auto-approval subscription.
|
|
92
|
+
*
|
|
93
|
+
* When `dryRun` is set, the network client and sender are resolved and the
|
|
94
|
+
* params validated, but nothing is added or broadcast — the resolved plan is
|
|
95
|
+
* returned so the CLI can preview it before the user confirms.
|
|
96
|
+
*
|
|
97
|
+
* @param messenger - The wallet root messenger.
|
|
98
|
+
* @param params - Validated `sendTransaction` params.
|
|
99
|
+
* @returns The dry-run plan, or the broadcast hash with its id and status.
|
|
100
|
+
*/
|
|
101
|
+
export declare function runSendTransaction(messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>, params: SendTransactionParams): Promise<SendTransactionResult>;
|
|
102
|
+
//# sourceMappingURL=send-transaction.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-transaction.d.mts","sourceRoot":"","sources":["../../src/daemon/send-transaction.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,KAAK,EAAE,8BAA8B;AAGnD,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACd,yBAAyB;AAqB1B;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;EAmCvC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAE9E;;;;;GAKG;AACH,eAAO,MAAM,iCAAiC;;;;;;;;;;;;EAM5C,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,oCAAoC;;;;;;;;EAI/C,CAAC;AAEH,MAAM,MAAM,2BAA2B,GAAG,KAAK,CAC7C,OAAO,iCAAiC,CACzC,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG,KAAK,CAChD,OAAO,oCAAoC,CAC5C,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAC7B,2BAA2B,GAC3B,8BAA8B,CAAC;AAEnC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,QAAQ,CAAC,aAAa,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC,EACjE,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CAiEhC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { boolean, define, literal, nonempty, object, optional, refine, string
|
|
2
|
-
import { isValidHexAddress, StrictHexStruct } from
|
|
1
|
+
import { boolean, define, literal, nonempty, object, optional, refine, string } from "@metamask/superstruct";
|
|
2
|
+
import { isValidHexAddress, StrictHexStruct } from "@metamask/utils";
|
|
3
3
|
/**
|
|
4
4
|
* Origin recorded on daemon-initiated transactions. Mirrors
|
|
5
5
|
* `ORIGIN_METAMASK` from `@metamask/controller-utils` (a single string
|
|
@@ -140,4 +140,4 @@ export async function runSendTransaction(messenger, params) {
|
|
|
140
140
|
status: current?.status ?? 'submitted',
|
|
141
141
|
};
|
|
142
142
|
}
|
|
143
|
-
//# sourceMappingURL=send-transaction.
|
|
143
|
+
//# sourceMappingURL=send-transaction.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-transaction.mjs","sourceRoot":"","sources":["../../src/daemon/send-transaction.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n boolean,\n define,\n literal,\n nonempty,\n object,\n optional,\n refine,\n string,\n} from '@metamask/superstruct';\nimport type { Infer } from '@metamask/superstruct';\nimport { isValidHexAddress, StrictHexStruct } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\nimport type {\n DefaultActions,\n DefaultEvents,\n RootMessenger,\n} from '@metamask/wallet';\n\n/**\n * Origin recorded on daemon-initiated transactions. Mirrors\n * `ORIGIN_METAMASK` from `@metamask/controller-utils` (a single string\n * constant, inlined here to avoid pulling that whole package in as a\n * dependency for one value).\n */\nconst INTERNAL_ORIGIN = 'metamask';\n\n/**\n * Struct for a `0x`-prefixed 20-byte hex address. `isValidHexAddress` accepts\n * an all-lowercase address or a valid EIP-55 checksummed one, so a plain\n * lowercase paste works while a mistyped mixed-case address is rejected.\n */\nconst AddressStruct = define<Hex>('Address', (value) =>\n typeof value === 'string' && isValidHexAddress(value as Hex)\n ? true\n : 'Expected a 0x-prefixed 20-byte hex address',\n);\n\n/**\n * Params struct for the `sendTransaction` RPC method.\n *\n * The transaction fields (`to`, `value`, `data`, gas overrides) are canonical\n * `0x`-prefixed hex — any unit conversion (e.g. ether → wei) is the CLI\n * command's job, so the daemon boundary stays unambiguous. Exactly one of\n * `networkClientId` / `chainId` selects the network client; the refinement\n * below rejects supplying both or neither. `dryRun` short-circuits before\n * broadcast (see {@link runSendTransaction}).\n */\nexport const SendTransactionParamsStruct = refine(\n object({\n to: AddressStruct,\n from: optional(AddressStruct),\n value: optional(StrictHexStruct),\n data: optional(StrictHexStruct),\n gas: optional(StrictHexStruct),\n maxFeePerGas: optional(StrictHexStruct),\n maxPriorityFeePerGas: optional(StrictHexStruct),\n gasPrice: optional(StrictHexStruct),\n networkClientId: optional(nonempty(string())),\n chainId: optional(StrictHexStruct),\n dryRun: optional(boolean()),\n }),\n 'SendTransactionParams',\n (value) => {\n if (\n (value.networkClientId === undefined) ===\n (value.chainId === undefined)\n ) {\n return 'Exactly one of `networkClientId` or `chainId` must be provided';\n }\n // `gasPrice` (legacy) and the EIP-1559 fields are mutually exclusive:\n // `TransactionController` rejects the combination at broadcast, so reject\n // it here at the boundary instead — otherwise a dry-run and the\n // confirmation preview would show a plan that always fails at send.\n if (\n value.gasPrice !== undefined &&\n (value.maxFeePerGas !== undefined ||\n value.maxPriorityFeePerGas !== undefined)\n ) {\n return '`gasPrice` cannot be combined with `maxFeePerGas` or `maxPriorityFeePerGas`';\n }\n return true;\n },\n);\n\nexport type SendTransactionParams = Infer<typeof SendTransactionParamsStruct>;\n\n/**\n * The resolved plan a `dryRun` returns: the network client and sender the\n * daemon would use, without adding or broadcasting anything. Exported as a\n * struct so the CLI can validate the payload it reads back over JSON-RPC\n * (which erases types on the wire) rather than trusting its shape.\n */\nexport const SendTransactionDryRunResultStruct = object({\n dryRun: literal(true),\n from: StrictHexStruct,\n to: StrictHexStruct,\n value: StrictHexStruct,\n networkClientId: string(),\n});\n\n/**\n * The outcome of a broadcast send: the on-chain hash plus the id/status the\n * daemon tracks the transaction under. Exported as a struct for the same\n * client-side validation reason as {@link SendTransactionDryRunResultStruct}.\n */\nexport const SendTransactionBroadcastResultStruct = object({\n transactionHash: string(),\n transactionId: string(),\n status: string(),\n});\n\nexport type SendTransactionDryRunResult = Infer<\n typeof SendTransactionDryRunResultStruct\n>;\n\nexport type SendTransactionBroadcastResult = Infer<\n typeof SendTransactionBroadcastResultStruct\n>;\n\nexport type SendTransactionResult =\n | SendTransactionDryRunResult\n | SendTransactionBroadcastResult;\n\n/**\n * Add and broadcast a transaction through the daemon-hosted\n * `TransactionController`, returning a serializable result.\n *\n * `TransactionController:addTransaction` returns `{ transactionMeta, result }`\n * where `result` is a `Promise<hash>` that resolves once the transaction is\n * signed and broadcast. That promise is not JSON-serializable, so it cannot\n * travel back over the daemon's JSON-RPC socket via the generic `call`\n * dispatch — hence this dedicated handler, which awaits the broadcast\n * server-side and returns only the resolved hash (and id/status).\n *\n * The network client is resolved from `chainId` (via `NetworkController`) when\n * `networkClientId` is not given directly, and `from` defaults to the selected\n * account. The transaction is submitted as `isInternal` (the daemon is driven\n * only by its owner over the `0600` same-user socket), which skips\n * origin/permitted-account validation; its approval request is auto-accepted\n * by the daemon's auto-approval subscription.\n *\n * When `dryRun` is set, the network client and sender are resolved and the\n * params validated, but nothing is added or broadcast — the resolved plan is\n * returned so the CLI can preview it before the user confirms.\n *\n * @param messenger - The wallet root messenger.\n * @param params - Validated `sendTransaction` params.\n * @returns The dry-run plan, or the broadcast hash with its id and status.\n */\nexport async function runSendTransaction(\n messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>,\n params: SendTransactionParams,\n): Promise<SendTransactionResult> {\n const {\n to,\n value = '0x0',\n data,\n gas,\n maxFeePerGas,\n maxPriorityFeePerGas,\n gasPrice,\n networkClientId: networkClientIdParam,\n chainId,\n dryRun,\n } = params;\n\n // The struct guarantees exactly one of `networkClientId` / `chainId`, so\n // `chainId` is defined whenever `networkClientId` is not.\n const networkClientId =\n networkClientIdParam ??\n messenger.call(\n 'NetworkController:findNetworkClientIdByChainId',\n chainId as Hex,\n );\n\n const from =\n params.from ??\n (messenger.call('AccountsController:getSelectedAccount').address as Hex);\n\n if (dryRun) {\n return { dryRun: true, from, to, value, networkClientId };\n }\n\n const txParams = {\n from,\n to,\n value,\n ...(data === undefined ? {} : { data }),\n ...(gas === undefined ? {} : { gas }),\n ...(maxFeePerGas === undefined ? {} : { maxFeePerGas }),\n ...(maxPriorityFeePerGas === undefined ? {} : { maxPriorityFeePerGas }),\n ...(gasPrice === undefined ? {} : { gasPrice }),\n };\n\n const { result, transactionMeta } = await messenger.call(\n 'TransactionController:addTransaction',\n txParams,\n { networkClientId, origin: INTERNAL_ORIGIN, isInternal: true },\n );\n\n // `result` resolves only once the transaction is signed and broadcast (see\n // the function JSDoc); awaiting it here is what this handler exists for.\n const transactionHash = await result;\n\n // Re-read the live record for the current status: `transactionMeta` is the\n // creation snapshot (still `unapproved`). If the record is already gone,\n // fall back to `submitted` — reaching this point means `result` resolved, so\n // the transaction was broadcast, never merely `unapproved`.\n const [current] = messenger.call('TransactionController:getTransactions', {\n searchCriteria: { id: transactionMeta.id },\n });\n\n return {\n transactionHash,\n transactionId: transactionMeta.id,\n status: current?.status ?? 'submitted',\n };\n}\n"]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readLine = exports.writeLine = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Write a newline-delimited line to a socket.
|
|
6
|
+
*
|
|
7
|
+
* @param socket - The socket to write to.
|
|
8
|
+
* @param line - The line to write (without trailing newline).
|
|
9
|
+
*/
|
|
10
|
+
async function writeLine(socket, line) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
// A socket 'error' can fire while the write is in flight without ever
|
|
13
|
+
// reaching the write callback; without this listener Node would treat it
|
|
14
|
+
// as an unhandled 'error' event and crash the process.
|
|
15
|
+
const onError = (error) => {
|
|
16
|
+
socket.removeListener('error', onError);
|
|
17
|
+
reject(error);
|
|
18
|
+
};
|
|
19
|
+
socket.once('error', onError);
|
|
20
|
+
socket.write(`${line}\n`, (error) => {
|
|
21
|
+
if (error) {
|
|
22
|
+
// A failed write (e.g. EPIPE) is delivered BOTH here AND as a later
|
|
23
|
+
// 'error' event. Leave `onError` attached to handle that event —
|
|
24
|
+
// detaching here would let Node treat it as unhandled and crash the
|
|
25
|
+
// process. This reject settles the promise; `onError` detaches itself
|
|
26
|
+
// when/if the event arrives.
|
|
27
|
+
reject(error);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
socket.removeListener('error', onError);
|
|
31
|
+
resolve();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
exports.writeLine = writeLine;
|
|
36
|
+
/**
|
|
37
|
+
* Read a single newline-delimited line from a socket.
|
|
38
|
+
*
|
|
39
|
+
* @param socket - The socket to read from.
|
|
40
|
+
* @param timeoutMs - Optional timeout in milliseconds. Rejects with a timeout
|
|
41
|
+
* error if no complete line is received within the limit.
|
|
42
|
+
* @returns The line read (without trailing newline).
|
|
43
|
+
*/
|
|
44
|
+
async function readLine(socket, timeoutMs) {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
let buffer = '';
|
|
47
|
+
let timer;
|
|
48
|
+
if (timeoutMs !== undefined) {
|
|
49
|
+
timer = setTimeout(() => {
|
|
50
|
+
cleanup();
|
|
51
|
+
reject(new Error('Socket read timed out'));
|
|
52
|
+
}, timeoutMs);
|
|
53
|
+
}
|
|
54
|
+
const onData = (data) => {
|
|
55
|
+
buffer += data.toString();
|
|
56
|
+
const idx = buffer.indexOf('\n');
|
|
57
|
+
if (idx !== -1) {
|
|
58
|
+
cleanup();
|
|
59
|
+
resolve(buffer.slice(0, idx));
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const onError = (error) => {
|
|
63
|
+
cleanup();
|
|
64
|
+
reject(error);
|
|
65
|
+
};
|
|
66
|
+
const onEnd = () => {
|
|
67
|
+
cleanup();
|
|
68
|
+
reject(new Error('Socket closed before response received'));
|
|
69
|
+
};
|
|
70
|
+
const onClose = () => {
|
|
71
|
+
cleanup();
|
|
72
|
+
reject(new Error('Socket closed before response received'));
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Remove listeners registered by this call and clear the timeout.
|
|
76
|
+
*/
|
|
77
|
+
function cleanup() {
|
|
78
|
+
if (timer !== undefined) {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
}
|
|
81
|
+
socket.removeListener('data', onData);
|
|
82
|
+
socket.removeListener('error', onError);
|
|
83
|
+
socket.removeListener('end', onEnd);
|
|
84
|
+
socket.removeListener('close', onClose);
|
|
85
|
+
}
|
|
86
|
+
socket.on('data', onData);
|
|
87
|
+
socket.once('error', onError);
|
|
88
|
+
socket.once('end', onEnd);
|
|
89
|
+
socket.once('close', onClose);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
exports.readLine = readLine;
|
|
93
|
+
//# sourceMappingURL=socket-line.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-line.cjs","sourceRoot":"","sources":["../../src/daemon/socket-line.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Socket } from 'node:net';\n\n/**\n * Write a newline-delimited line to a socket.\n *\n * @param socket - The socket to write to.\n * @param line - The line to write (without trailing newline).\n */\nexport async function writeLine(socket: Socket, line: string): Promise<void> {\n return new Promise((resolve, reject) => {\n // A socket 'error' can fire while the write is in flight without ever\n // reaching the write callback; without this listener Node would treat it\n // as an unhandled 'error' event and crash the process.\n const onError = (error: Error): void => {\n socket.removeListener('error', onError);\n reject(error);\n };\n socket.once('error', onError);\n\n socket.write(`${line}\\n`, (error) => {\n if (error) {\n // A failed write (e.g. EPIPE) is delivered BOTH here AND as a later\n // 'error' event. Leave `onError` attached to handle that event —\n // detaching here would let Node treat it as unhandled and crash the\n // process. This reject settles the promise; `onError` detaches itself\n // when/if the event arrives.\n reject(error);\n return;\n }\n socket.removeListener('error', onError);\n resolve();\n });\n });\n}\n\n/**\n * Read a single newline-delimited line from a socket.\n *\n * @param socket - The socket to read from.\n * @param timeoutMs - Optional timeout in milliseconds. Rejects with a timeout\n * error if no complete line is received within the limit.\n * @returns The line read (without trailing newline).\n */\nexport async function readLine(\n socket: Socket,\n timeoutMs?: number,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n let buffer = '';\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n if (timeoutMs !== undefined) {\n timer = setTimeout(() => {\n cleanup();\n reject(new Error('Socket read timed out'));\n }, timeoutMs);\n }\n\n const onData = (data: Buffer): void => {\n buffer += data.toString();\n const idx = buffer.indexOf('\\n');\n if (idx !== -1) {\n cleanup();\n resolve(buffer.slice(0, idx));\n }\n };\n\n const onError = (error: Error): void => {\n cleanup();\n reject(error);\n };\n\n const onEnd = (): void => {\n cleanup();\n reject(new Error('Socket closed before response received'));\n };\n\n const onClose = (): void => {\n cleanup();\n reject(new Error('Socket closed before response received'));\n };\n\n /**\n * Remove listeners registered by this call and clear the timeout.\n */\n function cleanup(): void {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n socket.removeListener('data', onData);\n socket.removeListener('error', onError);\n socket.removeListener('end', onEnd);\n socket.removeListener('close', onClose);\n }\n\n socket.on('data', onData);\n socket.once('error', onError);\n socket.once('end', onEnd);\n socket.once('close', onClose);\n });\n}\n"]}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node/net" />
|
|
3
|
+
/// <reference types="npm-which/node_modules/@types/node/net" />
|
|
4
|
+
import type { Socket } from "node:net";
|
|
2
5
|
/**
|
|
3
6
|
* Write a newline-delimited line to a socket.
|
|
4
7
|
*
|
|
@@ -15,4 +18,4 @@ export declare function writeLine(socket: Socket, line: string): Promise<void>;
|
|
|
15
18
|
* @returns The line read (without trailing newline).
|
|
16
19
|
*/
|
|
17
20
|
export declare function readLine(socket: Socket, timeoutMs?: number): Promise<string>;
|
|
18
|
-
//# sourceMappingURL=socket-line.d.
|
|
21
|
+
//# sourceMappingURL=socket-line.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-line.d.cts","sourceRoot":"","sources":["../../src/daemon/socket-line.ts"],"names":[],"mappings":";;;AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB;AAEvC;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3E;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAsDjB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node/net" />
|
|
3
|
+
/// <reference types="npm-which/node_modules/@types/node/net" />
|
|
4
|
+
import type { Socket } from "node:net";
|
|
5
|
+
/**
|
|
6
|
+
* Write a newline-delimited line to a socket.
|
|
7
|
+
*
|
|
8
|
+
* @param socket - The socket to write to.
|
|
9
|
+
* @param line - The line to write (without trailing newline).
|
|
10
|
+
*/
|
|
11
|
+
export declare function writeLine(socket: Socket, line: string): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Read a single newline-delimited line from a socket.
|
|
14
|
+
*
|
|
15
|
+
* @param socket - The socket to read from.
|
|
16
|
+
* @param timeoutMs - Optional timeout in milliseconds. Rejects with a timeout
|
|
17
|
+
* error if no complete line is received within the limit.
|
|
18
|
+
* @returns The line read (without trailing newline).
|
|
19
|
+
*/
|
|
20
|
+
export declare function readLine(socket: Socket, timeoutMs?: number): Promise<string>;
|
|
21
|
+
//# sourceMappingURL=socket-line.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-line.d.mts","sourceRoot":"","sources":["../../src/daemon/socket-line.ts"],"names":[],"mappings":";;;AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB;AAEvC;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3E;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAsDjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-line.mjs","sourceRoot":"","sources":["../../src/daemon/socket-line.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Socket } from 'node:net';\n\n/**\n * Write a newline-delimited line to a socket.\n *\n * @param socket - The socket to write to.\n * @param line - The line to write (without trailing newline).\n */\nexport async function writeLine(socket: Socket, line: string): Promise<void> {\n return new Promise((resolve, reject) => {\n // A socket 'error' can fire while the write is in flight without ever\n // reaching the write callback; without this listener Node would treat it\n // as an unhandled 'error' event and crash the process.\n const onError = (error: Error): void => {\n socket.removeListener('error', onError);\n reject(error);\n };\n socket.once('error', onError);\n\n socket.write(`${line}\\n`, (error) => {\n if (error) {\n // A failed write (e.g. EPIPE) is delivered BOTH here AND as a later\n // 'error' event. Leave `onError` attached to handle that event —\n // detaching here would let Node treat it as unhandled and crash the\n // process. This reject settles the promise; `onError` detaches itself\n // when/if the event arrives.\n reject(error);\n return;\n }\n socket.removeListener('error', onError);\n resolve();\n });\n });\n}\n\n/**\n * Read a single newline-delimited line from a socket.\n *\n * @param socket - The socket to read from.\n * @param timeoutMs - Optional timeout in milliseconds. Rejects with a timeout\n * error if no complete line is received within the limit.\n * @returns The line read (without trailing newline).\n */\nexport async function readLine(\n socket: Socket,\n timeoutMs?: number,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n let buffer = '';\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n if (timeoutMs !== undefined) {\n timer = setTimeout(() => {\n cleanup();\n reject(new Error('Socket read timed out'));\n }, timeoutMs);\n }\n\n const onData = (data: Buffer): void => {\n buffer += data.toString();\n const idx = buffer.indexOf('\\n');\n if (idx !== -1) {\n cleanup();\n resolve(buffer.slice(0, idx));\n }\n };\n\n const onError = (error: Error): void => {\n cleanup();\n reject(error);\n };\n\n const onEnd = (): void => {\n cleanup();\n reject(new Error('Socket closed before response received'));\n };\n\n const onClose = (): void => {\n cleanup();\n reject(new Error('Socket closed before response received'));\n };\n\n /**\n * Remove listeners registered by this call and clear the timeout.\n */\n function cleanup(): void {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n socket.removeListener('data', onData);\n socket.removeListener('error', onError);\n socket.removeListener('end', onEnd);\n socket.removeListener('close', onClose);\n }\n\n socket.on('data', onData);\n socket.once('error', onError);\n socket.once('end', onEnd);\n socket.once('close', onClose);\n });\n}\n"]}
|