@haven_ai/signer 0.0.0-dev.202609250651.afc2091 → 0.0.0-dev.202609250737.3bd5a51
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/README.md +13 -6
- package/dist/cli.cjs +27 -3
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +28 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +29 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +31 -7
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ It exposes four stdio MCP tools, all sign-only:
|
|
|
61
61
|
|
|
62
62
|
| Tool | Does | Emits |
|
|
63
63
|
|---|---|---|
|
|
64
|
-
| `haven_sign` | Sign one payment. Preferred form is `{ payment_id }` alone — the signer fetches the exact payload itself. Signs only Haven-prepared payloads (#3272): a direct-payment `PackedUserOperation` from this signer's own delegate account whose only call redeems a delegation made to that account,
|
|
64
|
+
| `haven_sign` | Sign one payment. Preferred form is `{ payment_id }` alone — the signer fetches the exact payload itself. Signs only Haven-prepared payloads (#3272, #3281): a direct-payment `PackedUserOperation` from this signer's own delegate account whose only call redeems a delegation made to that account, or — against a Haven-signed context, which it records and binds — an EIP-3009 funding leg of that same shape paying this signer's own delegate EOA, or an erc7710 settlement child from this signer's own account; other typed data is refused (`TYPED_DATA_NOT_ALLOWED`) | `{ signature }` or `{ signature, x402_binding }` |
|
|
65
65
|
| `haven_sign_x402` | One-shot x402: funding signature **and** the merchant header in a single local call (`haven_sign` + `haven_x402_sign_header`). `{ payment_id }` alone is the preferred call | `{ signature, x402_binding, payment_header, accepted }` |
|
|
66
66
|
| `haven_x402_sign_header` | Build + sign the EIP-3009 merchant payment header, only when the fresh merchant `payment_required` matches the recorded `x402_binding` | `{ payment_header, accepted }` |
|
|
67
67
|
| `haven_sign_sweep_delegate` | Sign a Haven-prepared gasless EIP-3009 sweep that recovers stranded funds from the delegate wallet back to your own account. Never broadcasts | `{ signature }` |
|
|
@@ -105,8 +105,11 @@ it signs whatever typed data it is handed. The allowlist lives in the MCP tool
|
|
|
105
105
|
layer: `haven_sign` signs typed data only when it is a bound direct-payment
|
|
106
106
|
`PackedUserOperation` (below) or an x402 payload against a Haven-signed context,
|
|
107
107
|
refuses anything else with `TYPED_DATA_NOT_ALLOWED`, and answers a bare
|
|
108
|
-
`payload_hash` with `BARE_HASH_REFUSED`. An embedder that calls
|
|
109
|
-
directly owns that check itself.
|
|
108
|
+
`payload_hash` with `BARE_HASH_REFUSED`. An embedder that calls
|
|
109
|
+
`signDelegationTypedData` directly owns that check itself. The core's
|
|
110
|
+
`signX402FundingTypedData` is not a verbatim primitive: since #3281 it runs
|
|
111
|
+
the x402 shape checks itself (a guarded funding leg or a verified settlement
|
|
112
|
+
child only), whoever calls it.
|
|
110
113
|
|
|
111
114
|
**The unbound-branch allowlist (#3272).** Without an x402 context, `haven_sign`
|
|
112
115
|
signs typed data only when ALL of these hold; otherwise it refuses, with no
|
|
@@ -144,9 +147,13 @@ operation, never that Haven prepared it — provenance is `payment_id` (below).
|
|
|
144
147
|
It runs whether the typed data arrived as a tool argument or by the
|
|
145
148
|
`payment_id` fetch; a fetched direct context that is not a
|
|
146
149
|
`PackedUserOperation` at all is refused by the same check. The x402 EIP-3009
|
|
147
|
-
bridge's funding leg
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
bridge's funding leg is checked against the Haven-signed expected context AND,
|
|
151
|
+
since #3281, by this same binding check against the declared UserOp hash, the
|
|
152
|
+
direct-payment allowlist and a recipient pin (a transfer of the quoted amount
|
|
153
|
+
to this signer's own delegate EOA). A settlement child must be re-delegated
|
|
154
|
+
from this signer's own account, never a ROOT grant. Anything else on the x402
|
|
155
|
+
arm is `TYPED_DATA_NOT_ALLOWED`, however validly Haven's binding key declared
|
|
156
|
+
it. The check exists because this typed data is
|
|
150
157
|
multi-KB and can reach the signer through a language model relaying it by
|
|
151
158
|
hand: one corrupted character used to produce a valid-looking signature over
|
|
152
159
|
the wrong digest, surfacing only as an opaque `AA24 signature error` from the
|
package/dist/cli.cjs
CHANGED
|
@@ -634,7 +634,17 @@ var toolDescriptions = {
|
|
|
634
634
|
haven_sign_sweep_delegate: SIGN_SWEEP_DELEGATE_DESCRIPTION
|
|
635
635
|
};
|
|
636
636
|
async function signFundingLeg(signer, expected, typedData) {
|
|
637
|
-
|
|
637
|
+
try {
|
|
638
|
+
return await signer.signX402FundingTypedData(typedData, expected);
|
|
639
|
+
} catch (err) {
|
|
640
|
+
if (err instanceof edge.HavenTypedDataRefusedError && !(err instanceof HavenTypedDataNotAllowedError)) {
|
|
641
|
+
throw new HavenTypedDataNotAllowedError(err.message);
|
|
642
|
+
}
|
|
643
|
+
if (err instanceof edge.HavenUserOpBindingError) {
|
|
644
|
+
throw new HavenUserOpBindingRefusedError(err.message);
|
|
645
|
+
}
|
|
646
|
+
throw err;
|
|
647
|
+
}
|
|
638
648
|
}
|
|
639
649
|
function toExpectedX402(raw) {
|
|
640
650
|
return {
|
|
@@ -1222,7 +1232,7 @@ function createEdgeSigner(delegateKey, options = {}) {
|
|
|
1222
1232
|
`Refusing to sign the x402 settlement child: this signer cannot map the network '${expected.network}' to a chain id, so it cannot check which chain the child is scoped to. Update @haven_ai/signer.`
|
|
1223
1233
|
);
|
|
1224
1234
|
}
|
|
1225
|
-
edge.
|
|
1235
|
+
edge.assertOwnSettlementChild(typedData, {
|
|
1226
1236
|
merchantTo: expected.merchantTo,
|
|
1227
1237
|
amount: expected.amount,
|
|
1228
1238
|
asset: expected.asset,
|
|
@@ -1232,7 +1242,21 @@ function createEdgeSigner(delegateKey, options = {}) {
|
|
|
1232
1242
|
// signed another" class this file exists to catch (#1455 review).
|
|
1233
1243
|
chainId: settlementChainId,
|
|
1234
1244
|
expiresAt: expected.expiresAt
|
|
1245
|
+
}, delegateAddress);
|
|
1246
|
+
} else if (edge.isPackedUserOperationTypedData(typedData)) {
|
|
1247
|
+
edge.assertUserOpTypedDataBinding(typedData, expected.payloadHash);
|
|
1248
|
+
edge.assertBoundDirectPaymentUserOp(typedData, delegateAddress);
|
|
1249
|
+
edge.assertFundingLegPaysDelegate(typedData, {
|
|
1250
|
+
delegateAddress,
|
|
1251
|
+
asset: expected.asset,
|
|
1252
|
+
amount: expected.amount
|
|
1235
1253
|
});
|
|
1254
|
+
} else {
|
|
1255
|
+
throw new edge.HavenTypedDataRefusedError(
|
|
1256
|
+
`This signer's x402 arm signs only a funding-leg PackedUserOperation or an erc7710 settlement Delegation; this typed data is '${String(
|
|
1257
|
+
typedData.primaryType
|
|
1258
|
+
)}'. Refusing, even though its digest is bound by the expected context.`
|
|
1259
|
+
);
|
|
1236
1260
|
}
|
|
1237
1261
|
const account = accounts.privateKeyToAccount(delegateKey);
|
|
1238
1262
|
const signature = await account.signTypedData(
|
|
@@ -1639,7 +1663,7 @@ async function warnIfCredentialFilePermissive(path, log = (message) => process.s
|
|
|
1639
1663
|
|
|
1640
1664
|
// src/server.ts
|
|
1641
1665
|
var SIGNER_NAME = "@haven_ai/signer";
|
|
1642
|
-
var SIGNER_VERSION = "0.0.0-dev.
|
|
1666
|
+
var SIGNER_VERSION = "0.0.0-dev.202609250737.3bd5a51";
|
|
1643
1667
|
async function resolveSignerRuntime(options = {}) {
|
|
1644
1668
|
assertSupportedNodeVersion(options.nodeVersion);
|
|
1645
1669
|
if (options.delegateKey) {
|