@haven_ai/sdk 0.3.0-alpha.0 → 0.5.0-alpha.1

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.
@@ -0,0 +1,105 @@
1
+ export { M as AgentPaymentFailureCode, Q as AgentPaymentNextAction, b_ as CAVEAT_ENFORCERS, b$ as DELEGATION_MANAGER, c0 as DIRECT_PAYMENT_CHAIN_IDS, a7 as DIRECT_SIGN_CONTEXT_VERSION, a8 as ENTRY_POINT_V07, c1 as EXECUTE_ABI, aa as HAVEN_CLIENT_HEADER, ab as HAVEN_CONNECTOR_CHANNEL, ac as HAVEN_MINIMUM_NODE_VERSION, c2 as HYBRID_DELEGATOR_IMPLEMENTATION, ai as HavenApiError, a as HavenClientUpdate, E as HavenError, am as HavenSigningError, ao as HavenTypedDataRefusedError, ap as HavenUnsupportedSignerVersionError, aq as HavenUserOpBindingError, c3 as MAX_SETTLEMENT_WINDOW_SECONDS, aw as NextStep, ay as NextStepHandoff, aA as NextStepTarget, aI as ROOT_AUTHORITY, aL as SIGNER_UPDATE_FALLBACK, c4 as SIMPLE_FACTORY_ADDRESS, aQ as SettlementChildExpectation, c5 as SettlementChildTypedData, aR as SignerRefusalCode, j as SweepAuthorization, aV as SweepExpectedAuth, a_ as TYPED_DATA_NOT_ALLOWED, b1 as X402ExpectedAuth, b5 as X402PaymentOption, X as X402PaymentRequired, bg as assertBoundDirectPaymentUserOp, c6 as assertFundingLegPaysDelegate, c7 as assertOwnSettlementChild, bh as assertUserOpTypedDataBinding, bi as buildSweepAuthorizationMessage, bj as buildSweepTypedData, bk as buildX402ExpectedMessage, c8 as chainIdForNetwork, bm as connectorRerunCommand, bo as createNextStepBuilder, bp as decodeBase64Json, bs as deriveDelegateAccountAddress, bt as encodeBase64Json, bw as havenClientIdentity, bz as isPackedUserOperationTypedData, c9 as isSettlementChildTypedData, bA as isSupportedNodeVersion, bD as packedUserOperationHash, bH as readClientUpdate, bN as selectStandardPaymentOption, bS as toStandardPaymentRequirements, bT as unsupportedNodeVersionMessage, bV as verifySettlementChild, bX as x402AuthorizationAmount, bZ as x402V2PaymentEnvelope } from './base64-BUMXmruY.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
+
41
+ /** `Delegation` — `(address delegate, address delegator, bytes32 authority, Caveat[] caveats, uint256 salt, bytes signature)`. */
42
+ declare const DELEGATION_TUPLE_COMPONENTS: readonly [{
43
+ readonly name: "delegate";
44
+ readonly type: "address";
45
+ }, {
46
+ readonly name: "delegator";
47
+ readonly type: "address";
48
+ }, {
49
+ readonly name: "authority";
50
+ readonly type: "bytes32";
51
+ }, {
52
+ readonly name: "caveats";
53
+ readonly type: "tuple[]";
54
+ readonly components: readonly [{
55
+ readonly name: "enforcer";
56
+ readonly type: "address";
57
+ }, {
58
+ readonly name: "terms";
59
+ readonly type: "bytes";
60
+ }, {
61
+ readonly name: "args";
62
+ readonly type: "bytes";
63
+ }];
64
+ }, {
65
+ readonly name: "salt";
66
+ readonly type: "uint256";
67
+ }, {
68
+ readonly name: "signature";
69
+ readonly type: "bytes";
70
+ }];
71
+ /** `DelegationManager.redeemDelegations(bytes[],bytes32[],bytes[])` — selector `0xcef6d209`. */
72
+ declare const REDEEM_DELEGATIONS_ABI: readonly [{
73
+ readonly type: "function";
74
+ readonly name: "redeemDelegations";
75
+ readonly inputs: readonly [{
76
+ readonly name: "_permissionContexts";
77
+ readonly type: "bytes[]";
78
+ }, {
79
+ readonly name: "_modes";
80
+ readonly type: "bytes32[]";
81
+ }, {
82
+ readonly name: "_executionCallDatas";
83
+ readonly type: "bytes[]";
84
+ }];
85
+ readonly outputs: readonly [];
86
+ readonly stateMutability: "nonpayable";
87
+ }];
88
+ /**
89
+ * `ExecutionMode.SingleDefault` — all-zero `bytes32`
90
+ * (`@metamask/smart-accounts-kit`'s `ExecutionMode` enum). The backend never
91
+ * emits any other mode (`delegation-rail.ts`'s `prepareRedemption` always
92
+ * passes `[ExecutionMode.SingleDefault]`), so any other value here is refused
93
+ * rather than interpreted.
94
+ */
95
+ declare const SINGLE_DEFAULT_MODE: Hex;
96
+ /**
97
+ * Verify `redeemCalldata` (the bytes passed to `execute()`, selector already
98
+ * confirmed to be `redeemDelegations` by the caller) is EXACTLY Haven's own
99
+ * shape: one delegation, redeemed by `ownAccount`, granted by someone else,
100
+ * in `SingleDefault` mode, with no encoding slack anywhere. Throws
101
+ * `HavenSigningError` (never returns) on any disagreement.
102
+ */
103
+ declare function assertRedeemsOwnBudgetDelegation(redeemCalldata: Hex, ownAccount: Address): void;
104
+
105
+ export { DELEGATION_TUPLE_COMPONENTS, REDEEM_DELEGATIONS_ABI, SINGLE_DEFAULT_MODE, addressFromKey, assertRedeemsOwnBudgetDelegation, signHash, verifySignature };
package/dist/edge.d.ts ADDED
@@ -0,0 +1,105 @@
1
+ export { M as AgentPaymentFailureCode, Q as AgentPaymentNextAction, b_ as CAVEAT_ENFORCERS, b$ as DELEGATION_MANAGER, c0 as DIRECT_PAYMENT_CHAIN_IDS, a7 as DIRECT_SIGN_CONTEXT_VERSION, a8 as ENTRY_POINT_V07, c1 as EXECUTE_ABI, aa as HAVEN_CLIENT_HEADER, ab as HAVEN_CONNECTOR_CHANNEL, ac as HAVEN_MINIMUM_NODE_VERSION, c2 as HYBRID_DELEGATOR_IMPLEMENTATION, ai as HavenApiError, a as HavenClientUpdate, E as HavenError, am as HavenSigningError, ao as HavenTypedDataRefusedError, ap as HavenUnsupportedSignerVersionError, aq as HavenUserOpBindingError, c3 as MAX_SETTLEMENT_WINDOW_SECONDS, aw as NextStep, ay as NextStepHandoff, aA as NextStepTarget, aI as ROOT_AUTHORITY, aL as SIGNER_UPDATE_FALLBACK, c4 as SIMPLE_FACTORY_ADDRESS, aQ as SettlementChildExpectation, c5 as SettlementChildTypedData, aR as SignerRefusalCode, j as SweepAuthorization, aV as SweepExpectedAuth, a_ as TYPED_DATA_NOT_ALLOWED, b1 as X402ExpectedAuth, b5 as X402PaymentOption, X as X402PaymentRequired, bg as assertBoundDirectPaymentUserOp, c6 as assertFundingLegPaysDelegate, c7 as assertOwnSettlementChild, bh as assertUserOpTypedDataBinding, bi as buildSweepAuthorizationMessage, bj as buildSweepTypedData, bk as buildX402ExpectedMessage, c8 as chainIdForNetwork, bm as connectorRerunCommand, bo as createNextStepBuilder, bp as decodeBase64Json, bs as deriveDelegateAccountAddress, bt as encodeBase64Json, bw as havenClientIdentity, bz as isPackedUserOperationTypedData, c9 as isSettlementChildTypedData, bA as isSupportedNodeVersion, bD as packedUserOperationHash, bH as readClientUpdate, bN as selectStandardPaymentOption, bS as toStandardPaymentRequirements, bT as unsupportedNodeVersionMessage, bV as verifySettlementChild, bX as x402AuthorizationAmount, bZ as x402V2PaymentEnvelope } from './base64-BUMXmruY.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
+
41
+ /** `Delegation` — `(address delegate, address delegator, bytes32 authority, Caveat[] caveats, uint256 salt, bytes signature)`. */
42
+ declare const DELEGATION_TUPLE_COMPONENTS: readonly [{
43
+ readonly name: "delegate";
44
+ readonly type: "address";
45
+ }, {
46
+ readonly name: "delegator";
47
+ readonly type: "address";
48
+ }, {
49
+ readonly name: "authority";
50
+ readonly type: "bytes32";
51
+ }, {
52
+ readonly name: "caveats";
53
+ readonly type: "tuple[]";
54
+ readonly components: readonly [{
55
+ readonly name: "enforcer";
56
+ readonly type: "address";
57
+ }, {
58
+ readonly name: "terms";
59
+ readonly type: "bytes";
60
+ }, {
61
+ readonly name: "args";
62
+ readonly type: "bytes";
63
+ }];
64
+ }, {
65
+ readonly name: "salt";
66
+ readonly type: "uint256";
67
+ }, {
68
+ readonly name: "signature";
69
+ readonly type: "bytes";
70
+ }];
71
+ /** `DelegationManager.redeemDelegations(bytes[],bytes32[],bytes[])` — selector `0xcef6d209`. */
72
+ declare const REDEEM_DELEGATIONS_ABI: readonly [{
73
+ readonly type: "function";
74
+ readonly name: "redeemDelegations";
75
+ readonly inputs: readonly [{
76
+ readonly name: "_permissionContexts";
77
+ readonly type: "bytes[]";
78
+ }, {
79
+ readonly name: "_modes";
80
+ readonly type: "bytes32[]";
81
+ }, {
82
+ readonly name: "_executionCallDatas";
83
+ readonly type: "bytes[]";
84
+ }];
85
+ readonly outputs: readonly [];
86
+ readonly stateMutability: "nonpayable";
87
+ }];
88
+ /**
89
+ * `ExecutionMode.SingleDefault` — all-zero `bytes32`
90
+ * (`@metamask/smart-accounts-kit`'s `ExecutionMode` enum). The backend never
91
+ * emits any other mode (`delegation-rail.ts`'s `prepareRedemption` always
92
+ * passes `[ExecutionMode.SingleDefault]`), so any other value here is refused
93
+ * rather than interpreted.
94
+ */
95
+ declare const SINGLE_DEFAULT_MODE: Hex;
96
+ /**
97
+ * Verify `redeemCalldata` (the bytes passed to `execute()`, selector already
98
+ * confirmed to be `redeemDelegations` by the caller) is EXACTLY Haven's own
99
+ * shape: one delegation, redeemed by `ownAccount`, granted by someone else,
100
+ * in `SingleDefault` mode, with no encoding slack anywhere. Throws
101
+ * `HavenSigningError` (never returns) on any disagreement.
102
+ */
103
+ declare function assertRedeemsOwnBudgetDelegation(redeemCalldata: Hex, ownAccount: Address): void;
104
+
105
+ 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 { DIRECT_PAYMENT_CHAIN_IDS, EXECUTE_ABI, HAVEN_CLIENT_HEADER, HAVEN_MINIMUM_NODE_VERSION, HavenTypedDataRefusedError, TYPED_DATA_NOT_ALLOWED, assertBoundDirectPaymentUserOp, assertFundingLegPaysDelegate, assertOwnSettlementChild, buildSweepAuthorizationMessage, buildSweepTypedData, buildX402ExpectedMessage, createNextStepBuilder, decodeBase64Json, encodeBase64Json, havenClientIdentity, isSupportedNodeVersion, readClientUpdate, selectStandardPaymentOption, toStandardPaymentRequirements, unsupportedNodeVersionMessage, x402AuthorizationAmount, x402V2PaymentEnvelope } from './chunk-KQ7JXKTV.js';
2
+ import { HavenSigningError } from './chunk-GLNG2GF7.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-GLNG2GF7.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":";;;;;;;AAsBA,IAAM,MAAA,GAAS,OAAO,oEAAoE,CAAA;AAW1F,SAAS,QAAA,CAAS,KAAA,EAAe,KAAA,EAAe,IAAA,EAAsB;AACpE,EAAA,IAAI,CAAC,mBAAmB,IAAA,CAAK,KAAK,KAAK,KAAA,CAAM,MAAA,KAAW,CAAA,GAAI,KAAA,GAAQ,CAAA,EAAG;AACrE,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,IAAI,CAAA,uBAAA,EAA0B,KAAK,CAAA,gBAAA,CAAkB,CAAA;AAAA,EAC1E;AACA,EAAA,OAAO,KAAA,CAAM,MAAM,CAAC,CAAA;AACtB;AAEO,SAAS,eAAe,UAAA,EAA4B;AACzD,EAAA,IAAI;AAEF,IAAA,OAAO,oBAAoB,CAAA,EAAA,EAAK,QAAA,CAAS,YAAY,EAAA,EAAI,aAAa,CAAC,CAAA,CAAE,CAAA;AAAA,EAC3E,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,IAAI,iBAAA;AAAA,MACR,wBAAwB,GAAA,YAAe,KAAA,GAAQ,IAAI,OAAA,GAAU,MAAA,CAAO,GAAG,CAAC,CAAA;AAAA,KAC1E;AAAA,EACF;AACF;AAEO,SAAS,QAAA,CAAS,YAAoB,IAAA,EAAsB;AACjE,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,GAAG,CAAA,EAAG,QAAA,KAAa,SAAA,CAAU,IAAA,CAAK,SAAS,IAAA,EAAM,EAAA,EAAI,MAAM,CAAA,EAAG,QAAA,CAAS,YAAY,EAAA,EAAI,aAAa,GAAG,EAAE,IAAA,EAAM,MAAM,CAAA;AAG7H,IAAA,OAAO,kBAAA,CAAmB;AAAA,MACxB,CAAA,EAAG,KAAK,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,EAAA,EAAI,GAAG,CAAC,CAAA,CAAA;AAAA,MACxC,CAAA,EAAG,KAAK,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,EAAA,EAAI,GAAG,CAAC,CAAA,CAAA;AAAA,MACxC,OAAA,EAAS;AAAA,KACV,CAAA;AAAA,EACH,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,IAAI,iBAAA;AAAA,MACR,wBAAwB,GAAA,YAAe,KAAA,GAAQ,IAAI,OAAA,GAAU,MAAA,CAAO,GAAG,CAAC,CAAA;AAAA,KAC1E;AAAA,EACF;AACF;AAEO,SAAS,eAAA,CAAgB,IAAA,EAAc,SAAA,EAAmB,eAAA,EAAkC;AACjG,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,QAAA,CAAS,SAAA,EAAW,EAAA,EAAI,WAAW,CAAA;AAC/C,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,IAAA,EAAM,EAAA,EAAI,MAAM,CAAA;AAEzC,IAAA,IAAI,MAAA,CAAO,CAAA,EAAA,EAAK,GAAA,CAAI,KAAA,CAAM,EAAA,EAAI,GAAG,CAAC,CAAA,CAAE,CAAA,GAAI,MAAA,EAAQ,OAAO,KAAA;AACvD,IAAA,MAAM,aAAa,MAAA,CAAO,QAAA,CAAS,IAAI,KAAA,CAAM,GAAG,GAAG,EAAE,CAAA;AACrD,IAAA,MAAM,WAAA,GAAc,UAAA,KAAe,CAAA,IAAK,UAAA,KAAe,EAAA,GAAK,IAAI,UAAA,KAAe,CAAA,IAAK,UAAA,KAAe,EAAA,GAAK,CAAA,GAAI,CAAA,CAAA;AAC5G,IAAA,IAAI,WAAA,GAAc,GAAG,OAAO,KAAA;AAC5B,IAAA,MAAM,YAAY,SAAA,CAAU,SAAA,CAAU,WAAA,CAAY,GAAA,CAAI,MAAM,CAAA,EAAG,GAAG,CAAC,CAAA,CAChE,eAAe,WAAW,CAAA,CAC1B,iBAAiB,OAAO,CAAA,CACxB,MAAM,KAAK,CAAA;AACd,IAAA,MAAM,SAAA,GAAY,kBAAA,CAAmB,CAAA,EAAA,EAAK,SAAS,CAAA,CAAE,CAAA;AACrD,IAAA,OAAO,SAAA,CAAU,WAAA,EAAY,KAAM,eAAA,CAAgB,WAAA,EAAY;AAAA,EACjE,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF","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"]}