@haven_ai/sdk 0.4.0-alpha.0 → 0.6.0-alpha.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/dist/edge.cjs ADDED
@@ -0,0 +1,299 @@
1
+ 'use strict';
2
+
3
+ var chunkCK4NLLMO_cjs = require('./chunk-CK4NLLMO.cjs');
4
+ var chunkS42QSWJY_cjs = require('./chunk-S42QSWJY.cjs');
5
+ var secp256k1 = require('@noble/curves/secp256k1');
6
+ var accounts = require('viem/accounts');
7
+ var viem = require('viem');
8
+
9
+ var HALF_N = BigInt("0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0");
10
+ function hexBytes(value, bytes, what) {
11
+ if (!/^0x[0-9a-fA-F]+$/.test(value) || value.length !== 2 + bytes * 2) {
12
+ throw new Error(`${what} must be a 0x-prefixed ${bytes}-byte hex string`);
13
+ }
14
+ return value.slice(2);
15
+ }
16
+ function addressFromKey(privateKey) {
17
+ try {
18
+ return accounts.privateKeyToAddress(`0x${hexBytes(privateKey, 32, "private key")}`);
19
+ } catch (err) {
20
+ throw new chunkS42QSWJY_cjs.HavenSigningError(
21
+ `Invalid private key: ${err instanceof Error ? err.message : String(err)}`
22
+ );
23
+ }
24
+ }
25
+ function signHash(privateKey, hash) {
26
+ try {
27
+ const { r, s, recovery } = secp256k1.secp256k1.sign(hexBytes(hash, 32, "hash"), hexBytes(privateKey, 32, "private key"), { lowS: true });
28
+ return viem.serializeSignature({
29
+ r: `0x${r.toString(16).padStart(64, "0")}`,
30
+ s: `0x${s.toString(16).padStart(64, "0")}`,
31
+ yParity: recovery
32
+ });
33
+ } catch (err) {
34
+ throw new chunkS42QSWJY_cjs.HavenSigningError(
35
+ `Failed to sign hash: ${err instanceof Error ? err.message : String(err)}`
36
+ );
37
+ }
38
+ }
39
+ function verifySignature(hash, signature, expectedAddress) {
40
+ try {
41
+ const sig = hexBytes(signature, 65, "signature");
42
+ const hashHex = hexBytes(hash, 32, "hash");
43
+ if (BigInt(`0x${sig.slice(64, 128)}`) > HALF_N) return false;
44
+ const yParityOrV = Number.parseInt(sig.slice(128), 16);
45
+ const recoveryBit = yParityOrV === 0 || yParityOrV === 27 ? 0 : yParityOrV === 1 || yParityOrV === 28 ? 1 : -1;
46
+ if (recoveryBit < 0) return false;
47
+ const publicKey = secp256k1.secp256k1.Signature.fromCompact(sig.slice(0, 128)).addRecoveryBit(recoveryBit).recoverPublicKey(hashHex).toHex(false);
48
+ const recovered = accounts.publicKeyToAddress(`0x${publicKey}`);
49
+ return recovered.toLowerCase() === expectedAddress.toLowerCase();
50
+ } catch {
51
+ return false;
52
+ }
53
+ }
54
+
55
+ Object.defineProperty(exports, "CAVEAT_TYPEHASH", {
56
+ enumerable: true,
57
+ get: function () { return chunkCK4NLLMO_cjs.CAVEAT_TYPEHASH; }
58
+ });
59
+ Object.defineProperty(exports, "DELEGATION_TYPEHASH", {
60
+ enumerable: true,
61
+ get: function () { return chunkCK4NLLMO_cjs.DELEGATION_TYPEHASH; }
62
+ });
63
+ Object.defineProperty(exports, "DIRECT_PAYMENT_CHAIN_IDS", {
64
+ enumerable: true,
65
+ get: function () { return chunkCK4NLLMO_cjs.DIRECT_PAYMENT_CHAIN_IDS; }
66
+ });
67
+ Object.defineProperty(exports, "EXECUTE_ABI", {
68
+ enumerable: true,
69
+ get: function () { return chunkCK4NLLMO_cjs.EXECUTE_ABI; }
70
+ });
71
+ Object.defineProperty(exports, "HAVEN_CLIENT_HEADER", {
72
+ enumerable: true,
73
+ get: function () { return chunkCK4NLLMO_cjs.HAVEN_CLIENT_HEADER; }
74
+ });
75
+ Object.defineProperty(exports, "HAVEN_MINIMUM_NODE_VERSION", {
76
+ enumerable: true,
77
+ get: function () { return chunkCK4NLLMO_cjs.HAVEN_MINIMUM_NODE_VERSION; }
78
+ });
79
+ Object.defineProperty(exports, "HavenTypedDataRefusedError", {
80
+ enumerable: true,
81
+ get: function () { return chunkCK4NLLMO_cjs.HavenTypedDataRefusedError; }
82
+ });
83
+ Object.defineProperty(exports, "MAX_TASK_BUDGET_TTL_SECONDS", {
84
+ enumerable: true,
85
+ get: function () { return chunkCK4NLLMO_cjs.MAX_TASK_BUDGET_TTL_SECONDS; }
86
+ });
87
+ Object.defineProperty(exports, "TYPED_DATA_NOT_ALLOWED", {
88
+ enumerable: true,
89
+ get: function () { return chunkCK4NLLMO_cjs.TYPED_DATA_NOT_ALLOWED; }
90
+ });
91
+ Object.defineProperty(exports, "assertBoundDirectPaymentUserOp", {
92
+ enumerable: true,
93
+ get: function () { return chunkCK4NLLMO_cjs.assertBoundDirectPaymentUserOp; }
94
+ });
95
+ Object.defineProperty(exports, "assertFundingLegPaysDelegate", {
96
+ enumerable: true,
97
+ get: function () { return chunkCK4NLLMO_cjs.assertFundingLegPaysDelegate; }
98
+ });
99
+ Object.defineProperty(exports, "assertOwnSettlementChild", {
100
+ enumerable: true,
101
+ get: function () { return chunkCK4NLLMO_cjs.assertOwnSettlementChild; }
102
+ });
103
+ Object.defineProperty(exports, "assertOwnTaskBudgetCloseUserOp", {
104
+ enumerable: true,
105
+ get: function () { return chunkCK4NLLMO_cjs.assertOwnTaskBudgetCloseUserOp; }
106
+ });
107
+ Object.defineProperty(exports, "assertOwnTaskChild", {
108
+ enumerable: true,
109
+ get: function () { return chunkCK4NLLMO_cjs.assertOwnTaskChild; }
110
+ });
111
+ Object.defineProperty(exports, "buildSweepAuthorizationMessage", {
112
+ enumerable: true,
113
+ get: function () { return chunkCK4NLLMO_cjs.buildSweepAuthorizationMessage; }
114
+ });
115
+ Object.defineProperty(exports, "buildSweepTypedData", {
116
+ enumerable: true,
117
+ get: function () { return chunkCK4NLLMO_cjs.buildSweepTypedData; }
118
+ });
119
+ Object.defineProperty(exports, "buildX402ExpectedMessage", {
120
+ enumerable: true,
121
+ get: function () { return chunkCK4NLLMO_cjs.buildX402ExpectedMessage; }
122
+ });
123
+ Object.defineProperty(exports, "createNextStepBuilder", {
124
+ enumerable: true,
125
+ get: function () { return chunkCK4NLLMO_cjs.createNextStepBuilder; }
126
+ });
127
+ Object.defineProperty(exports, "decodeBase64Json", {
128
+ enumerable: true,
129
+ get: function () { return chunkCK4NLLMO_cjs.decodeBase64Json; }
130
+ });
131
+ Object.defineProperty(exports, "encodeBase64Json", {
132
+ enumerable: true,
133
+ get: function () { return chunkCK4NLLMO_cjs.encodeBase64Json; }
134
+ });
135
+ Object.defineProperty(exports, "hashDelegation", {
136
+ enumerable: true,
137
+ get: function () { return chunkCK4NLLMO_cjs.hashDelegation; }
138
+ });
139
+ Object.defineProperty(exports, "havenClientIdentity", {
140
+ enumerable: true,
141
+ get: function () { return chunkCK4NLLMO_cjs.havenClientIdentity; }
142
+ });
143
+ Object.defineProperty(exports, "isSupportedNodeVersion", {
144
+ enumerable: true,
145
+ get: function () { return chunkCK4NLLMO_cjs.isSupportedNodeVersion; }
146
+ });
147
+ Object.defineProperty(exports, "isTaskChildTypedData", {
148
+ enumerable: true,
149
+ get: function () { return chunkCK4NLLMO_cjs.isTaskChildTypedData; }
150
+ });
151
+ Object.defineProperty(exports, "readClientUpdate", {
152
+ enumerable: true,
153
+ get: function () { return chunkCK4NLLMO_cjs.readClientUpdate; }
154
+ });
155
+ Object.defineProperty(exports, "selectStandardPaymentOption", {
156
+ enumerable: true,
157
+ get: function () { return chunkCK4NLLMO_cjs.selectStandardPaymentOption; }
158
+ });
159
+ Object.defineProperty(exports, "toStandardPaymentRequirements", {
160
+ enumerable: true,
161
+ get: function () { return chunkCK4NLLMO_cjs.toStandardPaymentRequirements; }
162
+ });
163
+ Object.defineProperty(exports, "unsupportedNodeVersionMessage", {
164
+ enumerable: true,
165
+ get: function () { return chunkCK4NLLMO_cjs.unsupportedNodeVersionMessage; }
166
+ });
167
+ Object.defineProperty(exports, "x402AuthorizationAmount", {
168
+ enumerable: true,
169
+ get: function () { return chunkCK4NLLMO_cjs.x402AuthorizationAmount; }
170
+ });
171
+ Object.defineProperty(exports, "x402V2PaymentEnvelope", {
172
+ enumerable: true,
173
+ get: function () { return chunkCK4NLLMO_cjs.x402V2PaymentEnvelope; }
174
+ });
175
+ Object.defineProperty(exports, "AgentPaymentFailureCode", {
176
+ enumerable: true,
177
+ get: function () { return chunkS42QSWJY_cjs.AgentPaymentFailureCode; }
178
+ });
179
+ Object.defineProperty(exports, "AgentPaymentNextAction", {
180
+ enumerable: true,
181
+ get: function () { return chunkS42QSWJY_cjs.AgentPaymentNextAction; }
182
+ });
183
+ Object.defineProperty(exports, "CAVEAT_ENFORCERS", {
184
+ enumerable: true,
185
+ get: function () { return chunkS42QSWJY_cjs.CAVEAT_ENFORCERS; }
186
+ });
187
+ Object.defineProperty(exports, "DELEGATION_MANAGER", {
188
+ enumerable: true,
189
+ get: function () { return chunkS42QSWJY_cjs.DELEGATION_MANAGER; }
190
+ });
191
+ Object.defineProperty(exports, "DELEGATION_TUPLE_COMPONENTS", {
192
+ enumerable: true,
193
+ get: function () { return chunkS42QSWJY_cjs.DELEGATION_TUPLE_COMPONENTS; }
194
+ });
195
+ Object.defineProperty(exports, "DIRECT_SIGN_CONTEXT_VERSION", {
196
+ enumerable: true,
197
+ get: function () { return chunkS42QSWJY_cjs.DIRECT_SIGN_CONTEXT_VERSION; }
198
+ });
199
+ Object.defineProperty(exports, "ENTRY_POINT_V07", {
200
+ enumerable: true,
201
+ get: function () { return chunkS42QSWJY_cjs.ENTRY_POINT_V07; }
202
+ });
203
+ Object.defineProperty(exports, "HAVEN_CONNECTOR_CHANNEL", {
204
+ enumerable: true,
205
+ get: function () { return chunkS42QSWJY_cjs.HAVEN_CONNECTOR_CHANNEL; }
206
+ });
207
+ Object.defineProperty(exports, "HYBRID_DELEGATOR_IMPLEMENTATION", {
208
+ enumerable: true,
209
+ get: function () { return chunkS42QSWJY_cjs.HYBRID_DELEGATOR_IMPLEMENTATION; }
210
+ });
211
+ Object.defineProperty(exports, "HavenApiError", {
212
+ enumerable: true,
213
+ get: function () { return chunkS42QSWJY_cjs.HavenApiError; }
214
+ });
215
+ Object.defineProperty(exports, "HavenError", {
216
+ enumerable: true,
217
+ get: function () { return chunkS42QSWJY_cjs.HavenError; }
218
+ });
219
+ Object.defineProperty(exports, "HavenSigningError", {
220
+ enumerable: true,
221
+ get: function () { return chunkS42QSWJY_cjs.HavenSigningError; }
222
+ });
223
+ Object.defineProperty(exports, "HavenUnsupportedSignerVersionError", {
224
+ enumerable: true,
225
+ get: function () { return chunkS42QSWJY_cjs.HavenUnsupportedSignerVersionError; }
226
+ });
227
+ Object.defineProperty(exports, "HavenUserOpBindingError", {
228
+ enumerable: true,
229
+ get: function () { return chunkS42QSWJY_cjs.HavenUserOpBindingError; }
230
+ });
231
+ Object.defineProperty(exports, "MAX_SETTLEMENT_WINDOW_SECONDS", {
232
+ enumerable: true,
233
+ get: function () { return chunkS42QSWJY_cjs.MAX_SETTLEMENT_WINDOW_SECONDS; }
234
+ });
235
+ Object.defineProperty(exports, "REDEEM_DELEGATIONS_ABI", {
236
+ enumerable: true,
237
+ get: function () { return chunkS42QSWJY_cjs.REDEEM_DELEGATIONS_ABI; }
238
+ });
239
+ Object.defineProperty(exports, "ROOT_AUTHORITY", {
240
+ enumerable: true,
241
+ get: function () { return chunkS42QSWJY_cjs.ROOT_AUTHORITY; }
242
+ });
243
+ Object.defineProperty(exports, "SIGNER_UPDATE_FALLBACK", {
244
+ enumerable: true,
245
+ get: function () { return chunkS42QSWJY_cjs.SIGNER_UPDATE_FALLBACK; }
246
+ });
247
+ Object.defineProperty(exports, "SIMPLE_FACTORY_ADDRESS", {
248
+ enumerable: true,
249
+ get: function () { return chunkS42QSWJY_cjs.SIMPLE_FACTORY_ADDRESS; }
250
+ });
251
+ Object.defineProperty(exports, "SINGLE_DEFAULT_MODE", {
252
+ enumerable: true,
253
+ get: function () { return chunkS42QSWJY_cjs.SINGLE_DEFAULT_MODE; }
254
+ });
255
+ Object.defineProperty(exports, "SignerRefusalCode", {
256
+ enumerable: true,
257
+ get: function () { return chunkS42QSWJY_cjs.SignerRefusalCode; }
258
+ });
259
+ Object.defineProperty(exports, "assertRedeemsOwnBudgetDelegation", {
260
+ enumerable: true,
261
+ get: function () { return chunkS42QSWJY_cjs.assertRedeemsOwnBudgetDelegation; }
262
+ });
263
+ Object.defineProperty(exports, "assertUserOpTypedDataBinding", {
264
+ enumerable: true,
265
+ get: function () { return chunkS42QSWJY_cjs.assertUserOpTypedDataBinding; }
266
+ });
267
+ Object.defineProperty(exports, "chainIdForNetwork", {
268
+ enumerable: true,
269
+ get: function () { return chunkS42QSWJY_cjs.chainIdForNetwork; }
270
+ });
271
+ Object.defineProperty(exports, "connectorRerunCommand", {
272
+ enumerable: true,
273
+ get: function () { return chunkS42QSWJY_cjs.connectorRerunCommand; }
274
+ });
275
+ Object.defineProperty(exports, "deriveDelegateAccountAddress", {
276
+ enumerable: true,
277
+ get: function () { return chunkS42QSWJY_cjs.deriveDelegateAccountAddress; }
278
+ });
279
+ Object.defineProperty(exports, "isPackedUserOperationTypedData", {
280
+ enumerable: true,
281
+ get: function () { return chunkS42QSWJY_cjs.isPackedUserOperationTypedData; }
282
+ });
283
+ Object.defineProperty(exports, "isSettlementChildTypedData", {
284
+ enumerable: true,
285
+ get: function () { return chunkS42QSWJY_cjs.isSettlementChildTypedData; }
286
+ });
287
+ Object.defineProperty(exports, "packedUserOperationHash", {
288
+ enumerable: true,
289
+ get: function () { return chunkS42QSWJY_cjs.packedUserOperationHash; }
290
+ });
291
+ Object.defineProperty(exports, "verifySettlementChild", {
292
+ enumerable: true,
293
+ get: function () { return chunkS42QSWJY_cjs.verifySettlementChild; }
294
+ });
295
+ exports.addressFromKey = addressFromKey;
296
+ exports.signHash = signHash;
297
+ exports.verifySignature = verifySignature;
298
+ //# sourceMappingURL=edge.cjs.map
299
+ //# sourceMappingURL=edge.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/edge-signing.ts"],"names":["privateKeyToAddress","HavenSigningError","secp256k1","serializeSignature","publicKeyToAddress"],"mappings":"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","file":"edge.cjs","sourcesContent":["import { secp256k1 } from '@noble/curves/secp256k1'\nimport { privateKeyToAddress, publicKeyToAddress } from 'viem/accounts'\nimport { serializeSignature, type Hex } from 'viem'\nimport { HavenSigningError } from './types.js'\n\n/**\n * #3173: the three key helpers the edge signer needs, without ethers.\n *\n * `./signer.ts` implements the same three on ethers, and the SDK barrel pulls\n * ethers in for them (and for `provider.ts` / `receipt.ts`). The edge signer\n * already ships viem, and viem signs with `@noble/curves` underneath — these\n * use the same curve library directly, synchronously (viem's own `sign` is\n * async), so `@haven_ai/sdk/edge` stays ethers-free and a signer session stops\n * paying for module init it never uses. Byte-equivalence with the ethers forms\n * is pinned in `edge-signing.test.ts`: same address, same 65-byte `r‖s‖v`\n * serialisation with low-s and v = 27/28, same recovery verdicts, and the same\n * refusals (high-s twin, wrong lengths) — with three stated strict-direction\n * exceptions, see `hexBytes`. Raw ECDSA over the hash — never the EIP-191\n * prefixed digest.\n */\n\n/** secp256k1 order / 2 — a signature with s above this is the malleable twin ethers rejects. */\nconst HALF_N = BigInt('0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0')\n\n/**\n * Stricter than ethers, never looser: the lowercase `0x` prefix and the exact\n * byte length are required. Ethers accepts an uppercase `0X` prefix on a\n * signature and the 64-byte EIP-2098 compact form, and is inconsistent on a\n * private key (`Wallet` accepts unprefixed and refuses `0X`; `SigningKey` does\n * the reverse); every such form is refused here — the divergences are pinned\n * in `edge-signing.test.ts` with ethers control assertions, and every\n * well-formed input is byte-equivalent.\n */\nfunction hexBytes(value: string, bytes: number, what: string): string {\n if (!/^0x[0-9a-fA-F]+$/.test(value) || value.length !== 2 + bytes * 2) {\n throw new Error(`${what} must be a 0x-prefixed ${bytes}-byte hex string`)\n }\n return value.slice(2)\n}\n\nexport function addressFromKey(privateKey: string): string {\n try {\n // Same gate as signHash: a key that derives an address must also sign.\n return privateKeyToAddress(`0x${hexBytes(privateKey, 32, 'private key')}`)\n } catch (err) {\n throw new HavenSigningError(\n `Invalid private key: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n}\n\nexport function signHash(privateKey: string, hash: string): string {\n try {\n const { r, s, recovery } = secp256k1.sign(hexBytes(hash, 32, 'hash'), hexBytes(privateKey, 32, 'private key'), { lowS: true })\n // viem serialises the trailing byte from yParity (0/1 → 27/28), the same\n // `v` ethers' `Signature.serialized` writes.\n return serializeSignature({\n r: `0x${r.toString(16).padStart(64, '0')}`,\n s: `0x${s.toString(16).padStart(64, '0')}`,\n yParity: recovery,\n })\n } catch (err) {\n throw new HavenSigningError(\n `Failed to sign hash: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n}\n\nexport function verifySignature(hash: string, signature: string, expectedAddress: string): boolean {\n try {\n const sig = hexBytes(signature, 65, 'signature')\n const hashHex = hexBytes(hash, 32, 'hash')\n // Reject the high-s malleable twin, as ethers does — one signature per message.\n if (BigInt(`0x${sig.slice(64, 128)}`) > HALF_N) return false\n const yParityOrV = Number.parseInt(sig.slice(128), 16)\n const recoveryBit = yParityOrV === 0 || yParityOrV === 27 ? 0 : yParityOrV === 1 || yParityOrV === 28 ? 1 : -1\n if (recoveryBit < 0) return false\n const publicKey = secp256k1.Signature.fromCompact(sig.slice(0, 128))\n .addRecoveryBit(recoveryBit)\n .recoverPublicKey(hashHex)\n .toHex(false)\n const recovered = publicKeyToAddress(`0x${publicKey}`)\n return recovered.toLowerCase() === expectedAddress.toLowerCase()\n } catch {\n return false\n }\n}\n"]}
@@ -0,0 +1,125 @@
1
+ export { U as AgentPaymentFailureCode, Y as AgentPaymentNextAction, cb as CAVEAT_ENFORCERS, cc as CAVEAT_TYPEHASH, cd as DELEGATION_MANAGER, ce as DELEGATION_TYPEHASH, cf as DIRECT_PAYMENT_CHAIN_IDS, ac as DIRECT_SIGN_CONTEXT_VERSION, cg as DelegationForHashing, ad as ENTRY_POINT_V07, ch as EXECUTE_ABI, af as HAVEN_CLIENT_HEADER, ag as HAVEN_CONNECTOR_CHANNEL, ah as HAVEN_MINIMUM_NODE_VERSION, ci as HYBRID_DELEGATOR_IMPLEMENTATION, an as HavenApiError, a as HavenClientUpdate, I as HavenError, ar as HavenSigningError, at as HavenTypedDataRefusedError, au as HavenUnsupportedSignerVersionError, av as HavenUserOpBindingError, cj as MAX_SETTLEMENT_WINDOW_SECONDS, ax as MAX_TASK_BUDGET_TTL_SECONDS, aC as NextStep, aE as NextStepHandoff, aG as NextStepTarget, aO as ROOT_AUTHORITY, aR as SIGNER_UPDATE_FALLBACK, ck as SIMPLE_FACTORY_ADDRESS, aW as SettlementChildExpectation, cl as SettlementChildTypedData, aX as SignerRefusalCode, j as SweepAuthorization, a$ as SweepExpectedAuth, b4 as TYPED_DATA_NOT_ALLOWED, b5 as TaskBudgetCloseExpectation, b6 as TaskChildExpectation, b7 as TaskChildTypedData, ba as X402ExpectedAuth, be as X402PaymentOption, X as X402PaymentRequired, bp as assertBoundDirectPaymentUserOp, cm as assertFundingLegPaysDelegate, cn as assertOwnSettlementChild, bq as assertOwnTaskBudgetCloseUserOp, br as assertOwnTaskChild, bs as assertUserOpTypedDataBinding, bt as buildSweepAuthorizationMessage, bu as buildSweepTypedData, bv as buildX402ExpectedMessage, co as chainIdForNetwork, bx as connectorRerunCommand, bz as createNextStepBuilder, bA as decodeBase64Json, bD as deriveDelegateAccountAddress, bE as encodeBase64Json, bH as hashDelegation, bI as havenClientIdentity, bL as isPackedUserOperationTypedData, cp as isSettlementChildTypedData, bM as isSupportedNodeVersion, bO as isTaskChildTypedData, bQ as packedUserOperationHash, bU as readClientUpdate, b_ as selectStandardPaymentOption, c3 as toStandardPaymentRequirements, c4 as unsupportedNodeVersionMessage, c6 as verifySettlementChild, c8 as x402AuthorizationAmount, ca as x402V2PaymentEnvelope } from './base64-cD9kR4JL.cjs';
2
+ import { Hex, Address } from 'viem';
3
+ import 'x402/types';
4
+
5
+ declare function addressFromKey(privateKey: string): string;
6
+ declare function signHash(privateKey: string, hash: string): string;
7
+ declare function verifySignature(hash: string, signature: string, expectedAddress: string): boolean;
8
+
9
+ /**
10
+ * #3272 (B1 follow-up) — what `DelegationManager.redeemDelegations` actually
11
+ * does with the bytes `haven_sign`'s unbound branch allows through.
12
+ *
13
+ * `assertBoundDirectPaymentUserOp` (then in the signer's `tools.ts`, now
14
+ * `direct-payment-guard.ts`) used to stop at "the inner
15
+ * call decodes as `redeemDelegations`" — it never looked at the ARGUMENTS.
16
+ * MetaMask's DelegationManager (v1.3.0) treats an EMPTY permission context
17
+ * (an ABI-encoded `Delegation[]` of length 0) as self-authorised: with no
18
+ * delegation to check, it calls `IDeleGatorCore(msg.sender).executeFromExecutor`
19
+ * directly — running the paired `executionCallData` AS THE ACCOUNT, with no
20
+ * caveat, no budget, no recipient pin. Since `HybridDeleGator.transferOwnership`
21
+ * / `updateSigners` / `addKey` are `onlyEntryPointOrSelf`, a self-call in that
22
+ * slot captures the account exactly as a bare `execute(self, 0,
23
+ * transferOwnership(attacker))` would — the redeemDelegations wrapper was
24
+ * never inspected past its own selector.
25
+ *
26
+ * This module decodes and verifies the REAL redemption shape Haven's backend
27
+ * emits (`packages/backend/src/rails/delegation-rail.ts` `prepareRedemption`):
28
+ * exactly one permission context, exactly one delegation in it, whose
29
+ * `delegate` is this signer's own account (so the redemption authority is
30
+ * this signer's, not forwarded from elsewhere) and whose `delegator` is a
31
+ * DIFFERENT address (so it is a real grant, not a no-op self-delegation),
32
+ * under `ExecutionMode.SingleDefault`, with no trailing or non-canonical
33
+ * bytes anywhere in the chain.
34
+ *
35
+ * The `Delegation`/`Caveat` tuple shape is vendored from
36
+ * `@metamask/delegation-abis`' `IDelegationManager` ABI (a devDependency
37
+ * only, in `@haven_ai/signer`'s `package.json`) and pinned against the kit's
38
+ * own encoder by `packages/signer/src/redemption-guard.pins.test.ts`.
39
+ *
40
+ * TASK BUDGETS (#3329) widen the accepted chain from exactly one link to ONE
41
+ * OR TWO: `[budget]` (unchanged) or `[task child, budget]` — leaf first, as
42
+ * everywhere else in this file. The two-link case is safe by the SAME
43
+ * reasoning as the one-link case, extended one hop: the task child's own
44
+ * `delegate` AND `delegator` are this signer's OWN account (a
45
+ * SELF-delegation — `assertOwnTaskChild` in `task-budget-guards.ts` is what
46
+ * proves its caveats narrow, never widen, the budget it chains under), and
47
+ * the budget link underneath it keeps the existing (3)/(4) shape: delegate ==
48
+ * own account, delegator != own account (a real grant from elsewhere). A
49
+ * chain redeemed this way can therefore only ever be AS OR MORE restrictive
50
+ * than redeeming the budget directly — the task child ADDS caveats (amount,
51
+ * recipient, expiry); it can never remove one, because
52
+ * `DelegationManager.redeemDelegations` ANDs every caveat of every link in
53
+ * the chain (the same AND-only property `settlement-child.ts`'s header
54
+ * documents). Any OTHER two-link chain — a leaf delegated by a third party, a
55
+ * leaf whose delegate is not this signer's own account, a chain longer than
56
+ * two — is refused exactly as before.
57
+ */
58
+
59
+ /** `Delegation` — `(address delegate, address delegator, bytes32 authority, Caveat[] caveats, uint256 salt, bytes signature)`. */
60
+ declare const DELEGATION_TUPLE_COMPONENTS: readonly [{
61
+ readonly name: "delegate";
62
+ readonly type: "address";
63
+ }, {
64
+ readonly name: "delegator";
65
+ readonly type: "address";
66
+ }, {
67
+ readonly name: "authority";
68
+ readonly type: "bytes32";
69
+ }, {
70
+ readonly name: "caveats";
71
+ readonly type: "tuple[]";
72
+ readonly components: readonly [{
73
+ readonly name: "enforcer";
74
+ readonly type: "address";
75
+ }, {
76
+ readonly name: "terms";
77
+ readonly type: "bytes";
78
+ }, {
79
+ readonly name: "args";
80
+ readonly type: "bytes";
81
+ }];
82
+ }, {
83
+ readonly name: "salt";
84
+ readonly type: "uint256";
85
+ }, {
86
+ readonly name: "signature";
87
+ readonly type: "bytes";
88
+ }];
89
+ /** `DelegationManager.redeemDelegations(bytes[],bytes32[],bytes[])` — selector `0xcef6d209`. */
90
+ declare const REDEEM_DELEGATIONS_ABI: readonly [{
91
+ readonly type: "function";
92
+ readonly name: "redeemDelegations";
93
+ readonly inputs: readonly [{
94
+ readonly name: "_permissionContexts";
95
+ readonly type: "bytes[]";
96
+ }, {
97
+ readonly name: "_modes";
98
+ readonly type: "bytes32[]";
99
+ }, {
100
+ readonly name: "_executionCallDatas";
101
+ readonly type: "bytes[]";
102
+ }];
103
+ readonly outputs: readonly [];
104
+ readonly stateMutability: "nonpayable";
105
+ }];
106
+ /**
107
+ * `ExecutionMode.SingleDefault` — all-zero `bytes32`
108
+ * (`@metamask/smart-accounts-kit`'s `ExecutionMode` enum). The backend never
109
+ * emits any other mode (`delegation-rail.ts`'s `prepareRedemption` always
110
+ * passes `[ExecutionMode.SingleDefault]`), so any other value here is refused
111
+ * rather than interpreted.
112
+ */
113
+ declare const SINGLE_DEFAULT_MODE: Hex;
114
+ /**
115
+ * Verify `redeemCalldata` (the bytes passed to `execute()`, selector already
116
+ * confirmed to be `redeemDelegations` by the caller) is EXACTLY one of Haven's
117
+ * two shapes: a single delegation redeemed by `ownAccount` and granted by
118
+ * someone else, or (#3329) that grant behind one task-budget child `ownAccount`
119
+ * delegated to itself; in `SingleDefault` mode, with no encoding slack
120
+ * anywhere. Throws
121
+ * `HavenSigningError` (never returns) on any disagreement.
122
+ */
123
+ declare function assertRedeemsOwnBudgetDelegation(redeemCalldata: Hex, ownAccount: Address): void;
124
+
125
+ export { DELEGATION_TUPLE_COMPONENTS, REDEEM_DELEGATIONS_ABI, SINGLE_DEFAULT_MODE, addressFromKey, assertRedeemsOwnBudgetDelegation, signHash, verifySignature };
package/dist/edge.d.ts ADDED
@@ -0,0 +1,125 @@
1
+ export { U as AgentPaymentFailureCode, Y as AgentPaymentNextAction, cb as CAVEAT_ENFORCERS, cc as CAVEAT_TYPEHASH, cd as DELEGATION_MANAGER, ce as DELEGATION_TYPEHASH, cf as DIRECT_PAYMENT_CHAIN_IDS, ac as DIRECT_SIGN_CONTEXT_VERSION, cg as DelegationForHashing, ad as ENTRY_POINT_V07, ch as EXECUTE_ABI, af as HAVEN_CLIENT_HEADER, ag as HAVEN_CONNECTOR_CHANNEL, ah as HAVEN_MINIMUM_NODE_VERSION, ci as HYBRID_DELEGATOR_IMPLEMENTATION, an as HavenApiError, a as HavenClientUpdate, I as HavenError, ar as HavenSigningError, at as HavenTypedDataRefusedError, au as HavenUnsupportedSignerVersionError, av as HavenUserOpBindingError, cj as MAX_SETTLEMENT_WINDOW_SECONDS, ax as MAX_TASK_BUDGET_TTL_SECONDS, aC as NextStep, aE as NextStepHandoff, aG as NextStepTarget, aO as ROOT_AUTHORITY, aR as SIGNER_UPDATE_FALLBACK, ck as SIMPLE_FACTORY_ADDRESS, aW as SettlementChildExpectation, cl as SettlementChildTypedData, aX as SignerRefusalCode, j as SweepAuthorization, a$ as SweepExpectedAuth, b4 as TYPED_DATA_NOT_ALLOWED, b5 as TaskBudgetCloseExpectation, b6 as TaskChildExpectation, b7 as TaskChildTypedData, ba as X402ExpectedAuth, be as X402PaymentOption, X as X402PaymentRequired, bp as assertBoundDirectPaymentUserOp, cm as assertFundingLegPaysDelegate, cn as assertOwnSettlementChild, bq as assertOwnTaskBudgetCloseUserOp, br as assertOwnTaskChild, bs as assertUserOpTypedDataBinding, bt as buildSweepAuthorizationMessage, bu as buildSweepTypedData, bv as buildX402ExpectedMessage, co as chainIdForNetwork, bx as connectorRerunCommand, bz as createNextStepBuilder, bA as decodeBase64Json, bD as deriveDelegateAccountAddress, bE as encodeBase64Json, bH as hashDelegation, bI as havenClientIdentity, bL as isPackedUserOperationTypedData, cp as isSettlementChildTypedData, bM as isSupportedNodeVersion, bO as isTaskChildTypedData, bQ as packedUserOperationHash, bU as readClientUpdate, b_ as selectStandardPaymentOption, c3 as toStandardPaymentRequirements, c4 as unsupportedNodeVersionMessage, c6 as verifySettlementChild, c8 as x402AuthorizationAmount, ca as x402V2PaymentEnvelope } from './base64-cD9kR4JL.js';
2
+ import { Hex, Address } from 'viem';
3
+ import 'x402/types';
4
+
5
+ declare function addressFromKey(privateKey: string): string;
6
+ declare function signHash(privateKey: string, hash: string): string;
7
+ declare function verifySignature(hash: string, signature: string, expectedAddress: string): boolean;
8
+
9
+ /**
10
+ * #3272 (B1 follow-up) — what `DelegationManager.redeemDelegations` actually
11
+ * does with the bytes `haven_sign`'s unbound branch allows through.
12
+ *
13
+ * `assertBoundDirectPaymentUserOp` (then in the signer's `tools.ts`, now
14
+ * `direct-payment-guard.ts`) used to stop at "the inner
15
+ * call decodes as `redeemDelegations`" — it never looked at the ARGUMENTS.
16
+ * MetaMask's DelegationManager (v1.3.0) treats an EMPTY permission context
17
+ * (an ABI-encoded `Delegation[]` of length 0) as self-authorised: with no
18
+ * delegation to check, it calls `IDeleGatorCore(msg.sender).executeFromExecutor`
19
+ * directly — running the paired `executionCallData` AS THE ACCOUNT, with no
20
+ * caveat, no budget, no recipient pin. Since `HybridDeleGator.transferOwnership`
21
+ * / `updateSigners` / `addKey` are `onlyEntryPointOrSelf`, a self-call in that
22
+ * slot captures the account exactly as a bare `execute(self, 0,
23
+ * transferOwnership(attacker))` would — the redeemDelegations wrapper was
24
+ * never inspected past its own selector.
25
+ *
26
+ * This module decodes and verifies the REAL redemption shape Haven's backend
27
+ * emits (`packages/backend/src/rails/delegation-rail.ts` `prepareRedemption`):
28
+ * exactly one permission context, exactly one delegation in it, whose
29
+ * `delegate` is this signer's own account (so the redemption authority is
30
+ * this signer's, not forwarded from elsewhere) and whose `delegator` is a
31
+ * DIFFERENT address (so it is a real grant, not a no-op self-delegation),
32
+ * under `ExecutionMode.SingleDefault`, with no trailing or non-canonical
33
+ * bytes anywhere in the chain.
34
+ *
35
+ * The `Delegation`/`Caveat` tuple shape is vendored from
36
+ * `@metamask/delegation-abis`' `IDelegationManager` ABI (a devDependency
37
+ * only, in `@haven_ai/signer`'s `package.json`) and pinned against the kit's
38
+ * own encoder by `packages/signer/src/redemption-guard.pins.test.ts`.
39
+ *
40
+ * TASK BUDGETS (#3329) widen the accepted chain from exactly one link to ONE
41
+ * OR TWO: `[budget]` (unchanged) or `[task child, budget]` — leaf first, as
42
+ * everywhere else in this file. The two-link case is safe by the SAME
43
+ * reasoning as the one-link case, extended one hop: the task child's own
44
+ * `delegate` AND `delegator` are this signer's OWN account (a
45
+ * SELF-delegation — `assertOwnTaskChild` in `task-budget-guards.ts` is what
46
+ * proves its caveats narrow, never widen, the budget it chains under), and
47
+ * the budget link underneath it keeps the existing (3)/(4) shape: delegate ==
48
+ * own account, delegator != own account (a real grant from elsewhere). A
49
+ * chain redeemed this way can therefore only ever be AS OR MORE restrictive
50
+ * than redeeming the budget directly — the task child ADDS caveats (amount,
51
+ * recipient, expiry); it can never remove one, because
52
+ * `DelegationManager.redeemDelegations` ANDs every caveat of every link in
53
+ * the chain (the same AND-only property `settlement-child.ts`'s header
54
+ * documents). Any OTHER two-link chain — a leaf delegated by a third party, a
55
+ * leaf whose delegate is not this signer's own account, a chain longer than
56
+ * two — is refused exactly as before.
57
+ */
58
+
59
+ /** `Delegation` — `(address delegate, address delegator, bytes32 authority, Caveat[] caveats, uint256 salt, bytes signature)`. */
60
+ declare const DELEGATION_TUPLE_COMPONENTS: readonly [{
61
+ readonly name: "delegate";
62
+ readonly type: "address";
63
+ }, {
64
+ readonly name: "delegator";
65
+ readonly type: "address";
66
+ }, {
67
+ readonly name: "authority";
68
+ readonly type: "bytes32";
69
+ }, {
70
+ readonly name: "caveats";
71
+ readonly type: "tuple[]";
72
+ readonly components: readonly [{
73
+ readonly name: "enforcer";
74
+ readonly type: "address";
75
+ }, {
76
+ readonly name: "terms";
77
+ readonly type: "bytes";
78
+ }, {
79
+ readonly name: "args";
80
+ readonly type: "bytes";
81
+ }];
82
+ }, {
83
+ readonly name: "salt";
84
+ readonly type: "uint256";
85
+ }, {
86
+ readonly name: "signature";
87
+ readonly type: "bytes";
88
+ }];
89
+ /** `DelegationManager.redeemDelegations(bytes[],bytes32[],bytes[])` — selector `0xcef6d209`. */
90
+ declare const REDEEM_DELEGATIONS_ABI: readonly [{
91
+ readonly type: "function";
92
+ readonly name: "redeemDelegations";
93
+ readonly inputs: readonly [{
94
+ readonly name: "_permissionContexts";
95
+ readonly type: "bytes[]";
96
+ }, {
97
+ readonly name: "_modes";
98
+ readonly type: "bytes32[]";
99
+ }, {
100
+ readonly name: "_executionCallDatas";
101
+ readonly type: "bytes[]";
102
+ }];
103
+ readonly outputs: readonly [];
104
+ readonly stateMutability: "nonpayable";
105
+ }];
106
+ /**
107
+ * `ExecutionMode.SingleDefault` — all-zero `bytes32`
108
+ * (`@metamask/smart-accounts-kit`'s `ExecutionMode` enum). The backend never
109
+ * emits any other mode (`delegation-rail.ts`'s `prepareRedemption` always
110
+ * passes `[ExecutionMode.SingleDefault]`), so any other value here is refused
111
+ * rather than interpreted.
112
+ */
113
+ declare const SINGLE_DEFAULT_MODE: Hex;
114
+ /**
115
+ * Verify `redeemCalldata` (the bytes passed to `execute()`, selector already
116
+ * confirmed to be `redeemDelegations` by the caller) is EXACTLY one of Haven's
117
+ * two shapes: a single delegation redeemed by `ownAccount` and granted by
118
+ * someone else, or (#3329) that grant behind one task-budget child `ownAccount`
119
+ * delegated to itself; in `SingleDefault` mode, with no encoding slack
120
+ * anywhere. Throws
121
+ * `HavenSigningError` (never returns) on any disagreement.
122
+ */
123
+ declare function assertRedeemsOwnBudgetDelegation(redeemCalldata: Hex, ownAccount: Address): void;
124
+
125
+ export { DELEGATION_TUPLE_COMPONENTS, REDEEM_DELEGATIONS_ABI, SINGLE_DEFAULT_MODE, addressFromKey, assertRedeemsOwnBudgetDelegation, signHash, verifySignature };
package/dist/edge.js ADDED
@@ -0,0 +1,56 @@
1
+ export { CAVEAT_TYPEHASH, DELEGATION_TYPEHASH, DIRECT_PAYMENT_CHAIN_IDS, EXECUTE_ABI, HAVEN_CLIENT_HEADER, HAVEN_MINIMUM_NODE_VERSION, HavenTypedDataRefusedError, MAX_TASK_BUDGET_TTL_SECONDS, TYPED_DATA_NOT_ALLOWED, assertBoundDirectPaymentUserOp, assertFundingLegPaysDelegate, assertOwnSettlementChild, assertOwnTaskBudgetCloseUserOp, assertOwnTaskChild, buildSweepAuthorizationMessage, buildSweepTypedData, buildX402ExpectedMessage, createNextStepBuilder, decodeBase64Json, encodeBase64Json, hashDelegation, havenClientIdentity, isSupportedNodeVersion, isTaskChildTypedData, readClientUpdate, selectStandardPaymentOption, toStandardPaymentRequirements, unsupportedNodeVersionMessage, x402AuthorizationAmount, x402V2PaymentEnvelope } from './chunk-3WSEX3GW.js';
2
+ import { HavenSigningError } from './chunk-63GPTEEY.js';
3
+ export { AgentPaymentFailureCode, AgentPaymentNextAction, CAVEAT_ENFORCERS, DELEGATION_MANAGER, DELEGATION_TUPLE_COMPONENTS, DIRECT_SIGN_CONTEXT_VERSION, ENTRY_POINT_V07, HAVEN_CONNECTOR_CHANNEL, HYBRID_DELEGATOR_IMPLEMENTATION, HavenApiError, HavenError, HavenSigningError, HavenUnsupportedSignerVersionError, HavenUserOpBindingError, MAX_SETTLEMENT_WINDOW_SECONDS, REDEEM_DELEGATIONS_ABI, ROOT_AUTHORITY, SIGNER_UPDATE_FALLBACK, SIMPLE_FACTORY_ADDRESS, SINGLE_DEFAULT_MODE, SignerRefusalCode, assertRedeemsOwnBudgetDelegation, assertUserOpTypedDataBinding, chainIdForNetwork, connectorRerunCommand, deriveDelegateAccountAddress, isPackedUserOperationTypedData, isSettlementChildTypedData, packedUserOperationHash, verifySettlementChild } from './chunk-63GPTEEY.js';
4
+ import { secp256k1 } from '@noble/curves/secp256k1';
5
+ import { privateKeyToAddress, publicKeyToAddress } from 'viem/accounts';
6
+ import { serializeSignature } from 'viem';
7
+
8
+ var HALF_N = BigInt("0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0");
9
+ function hexBytes(value, bytes, what) {
10
+ if (!/^0x[0-9a-fA-F]+$/.test(value) || value.length !== 2 + bytes * 2) {
11
+ throw new Error(`${what} must be a 0x-prefixed ${bytes}-byte hex string`);
12
+ }
13
+ return value.slice(2);
14
+ }
15
+ function addressFromKey(privateKey) {
16
+ try {
17
+ return privateKeyToAddress(`0x${hexBytes(privateKey, 32, "private key")}`);
18
+ } catch (err) {
19
+ throw new HavenSigningError(
20
+ `Invalid private key: ${err instanceof Error ? err.message : String(err)}`
21
+ );
22
+ }
23
+ }
24
+ function signHash(privateKey, hash) {
25
+ try {
26
+ const { r, s, recovery } = secp256k1.sign(hexBytes(hash, 32, "hash"), hexBytes(privateKey, 32, "private key"), { lowS: true });
27
+ return serializeSignature({
28
+ r: `0x${r.toString(16).padStart(64, "0")}`,
29
+ s: `0x${s.toString(16).padStart(64, "0")}`,
30
+ yParity: recovery
31
+ });
32
+ } catch (err) {
33
+ throw new HavenSigningError(
34
+ `Failed to sign hash: ${err instanceof Error ? err.message : String(err)}`
35
+ );
36
+ }
37
+ }
38
+ function verifySignature(hash, signature, expectedAddress) {
39
+ try {
40
+ const sig = hexBytes(signature, 65, "signature");
41
+ const hashHex = hexBytes(hash, 32, "hash");
42
+ if (BigInt(`0x${sig.slice(64, 128)}`) > HALF_N) return false;
43
+ const yParityOrV = Number.parseInt(sig.slice(128), 16);
44
+ const recoveryBit = yParityOrV === 0 || yParityOrV === 27 ? 0 : yParityOrV === 1 || yParityOrV === 28 ? 1 : -1;
45
+ if (recoveryBit < 0) return false;
46
+ const publicKey = secp256k1.Signature.fromCompact(sig.slice(0, 128)).addRecoveryBit(recoveryBit).recoverPublicKey(hashHex).toHex(false);
47
+ const recovered = publicKeyToAddress(`0x${publicKey}`);
48
+ return recovered.toLowerCase() === expectedAddress.toLowerCase();
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+
54
+ export { addressFromKey, signHash, verifySignature };
55
+ //# sourceMappingURL=edge.js.map
56
+ //# sourceMappingURL=edge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/edge-signing.ts"],"names":[],"mappings":"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","file":"edge.js","sourcesContent":["import { secp256k1 } from '@noble/curves/secp256k1'\nimport { privateKeyToAddress, publicKeyToAddress } from 'viem/accounts'\nimport { serializeSignature, type Hex } from 'viem'\nimport { HavenSigningError } from './types.js'\n\n/**\n * #3173: the three key helpers the edge signer needs, without ethers.\n *\n * `./signer.ts` implements the same three on ethers, and the SDK barrel pulls\n * ethers in for them (and for `provider.ts` / `receipt.ts`). The edge signer\n * already ships viem, and viem signs with `@noble/curves` underneath — these\n * use the same curve library directly, synchronously (viem's own `sign` is\n * async), so `@haven_ai/sdk/edge` stays ethers-free and a signer session stops\n * paying for module init it never uses. Byte-equivalence with the ethers forms\n * is pinned in `edge-signing.test.ts`: same address, same 65-byte `r‖s‖v`\n * serialisation with low-s and v = 27/28, same recovery verdicts, and the same\n * refusals (high-s twin, wrong lengths) — with three stated strict-direction\n * exceptions, see `hexBytes`. Raw ECDSA over the hash — never the EIP-191\n * prefixed digest.\n */\n\n/** secp256k1 order / 2 — a signature with s above this is the malleable twin ethers rejects. */\nconst HALF_N = BigInt('0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0')\n\n/**\n * Stricter than ethers, never looser: the lowercase `0x` prefix and the exact\n * byte length are required. Ethers accepts an uppercase `0X` prefix on a\n * signature and the 64-byte EIP-2098 compact form, and is inconsistent on a\n * private key (`Wallet` accepts unprefixed and refuses `0X`; `SigningKey` does\n * the reverse); every such form is refused here — the divergences are pinned\n * in `edge-signing.test.ts` with ethers control assertions, and every\n * well-formed input is byte-equivalent.\n */\nfunction hexBytes(value: string, bytes: number, what: string): string {\n if (!/^0x[0-9a-fA-F]+$/.test(value) || value.length !== 2 + bytes * 2) {\n throw new Error(`${what} must be a 0x-prefixed ${bytes}-byte hex string`)\n }\n return value.slice(2)\n}\n\nexport function addressFromKey(privateKey: string): string {\n try {\n // Same gate as signHash: a key that derives an address must also sign.\n return privateKeyToAddress(`0x${hexBytes(privateKey, 32, 'private key')}`)\n } catch (err) {\n throw new HavenSigningError(\n `Invalid private key: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n}\n\nexport function signHash(privateKey: string, hash: string): string {\n try {\n const { r, s, recovery } = secp256k1.sign(hexBytes(hash, 32, 'hash'), hexBytes(privateKey, 32, 'private key'), { lowS: true })\n // viem serialises the trailing byte from yParity (0/1 → 27/28), the same\n // `v` ethers' `Signature.serialized` writes.\n return serializeSignature({\n r: `0x${r.toString(16).padStart(64, '0')}`,\n s: `0x${s.toString(16).padStart(64, '0')}`,\n yParity: recovery,\n })\n } catch (err) {\n throw new HavenSigningError(\n `Failed to sign hash: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n}\n\nexport function verifySignature(hash: string, signature: string, expectedAddress: string): boolean {\n try {\n const sig = hexBytes(signature, 65, 'signature')\n const hashHex = hexBytes(hash, 32, 'hash')\n // Reject the high-s malleable twin, as ethers does — one signature per message.\n if (BigInt(`0x${sig.slice(64, 128)}`) > HALF_N) return false\n const yParityOrV = Number.parseInt(sig.slice(128), 16)\n const recoveryBit = yParityOrV === 0 || yParityOrV === 27 ? 0 : yParityOrV === 1 || yParityOrV === 28 ? 1 : -1\n if (recoveryBit < 0) return false\n const publicKey = secp256k1.Signature.fromCompact(sig.slice(0, 128))\n .addRecoveryBit(recoveryBit)\n .recoverPublicKey(hashHex)\n .toHex(false)\n const recovered = publicKeyToAddress(`0x${publicKey}`)\n return recovered.toLowerCase() === expectedAddress.toLowerCase()\n } catch {\n return false\n }\n}\n"]}