@haven_ai/sdk 0.0.0-dev.202610142033.0d2f50f → 0.0.0-dev.202610151444.5ef2d94
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/{base64-BL0Su-Ui.d.cts → base64-DOKNqZIt.d.cts} +2 -2
- package/dist/{base64-DlJYkxS0.d.ts → base64-xbbjMr2w.d.ts} +2 -2
- package/dist/{chunk-OOQT7OGF.js → chunk-2BIAJVXZ.js} +3 -3
- package/dist/{chunk-OOQT7OGF.js.map → chunk-2BIAJVXZ.js.map} +1 -1
- package/dist/{chunk-EZA47UQ5.cjs → chunk-4BCOX5AL.cjs} +4 -4
- package/dist/{chunk-EZA47UQ5.cjs.map → chunk-4BCOX5AL.cjs.map} +1 -1
- package/dist/{chunk-ECP76EZP.cjs → chunk-CM4AZGVP.cjs} +19 -3
- package/dist/{chunk-ECP76EZP.cjs.map → chunk-CM4AZGVP.cjs.map} +1 -1
- package/dist/{chunk-D4FF4STI.js → chunk-T3UQTTZR.js} +19 -3
- package/dist/{chunk-D4FF4STI.js.map → chunk-T3UQTTZR.js.map} +1 -1
- package/dist/{chunk-PF4CEJ2M.js → chunk-X3OCDYFT.js} +4 -4
- package/dist/{chunk-PF4CEJ2M.js.map → chunk-X3OCDYFT.js.map} +1 -1
- package/dist/{chunk-KHRIJTBO.cjs → chunk-Y2ONOEVW.cjs} +35 -35
- package/dist/{chunk-KHRIJTBO.cjs.map → chunk-Y2ONOEVW.cjs.map} +1 -1
- package/dist/edge.cjs +93 -93
- package/dist/edge.d.cts +4 -4
- package/dist/edge.d.ts +4 -4
- package/dist/edge.js +4 -4
- package/dist/index.cjs +338 -325
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -7
- package/dist/index.d.ts +15 -7
- package/dist/index.js +21 -8
- package/dist/index.js.map +1 -1
- package/dist/{receipt-BtvQj1Pq.d.ts → receipt-Cps1ghLb.d.ts} +1 -1
- package/dist/{receipt-CgNj6ZwD.d.cts → receipt-D1aOdZTN.d.cts} +1 -1
- package/dist/test-support.cjs +21 -21
- package/dist/test-support.d.cts +2 -2
- package/dist/test-support.d.ts +2 -2
- package/dist/test-support.js +2 -2
- package/dist/{types-C4evTSKK.d.ts → types-Cde7k2ay.d.cts} +16 -0
- package/dist/{types-C4evTSKK.d.cts → types-Cde7k2ay.d.ts} +16 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Q as AgentNextStep, Z as AgentPaymentNextAction, aM as X402PaymentOption, aL as X402ExpectedContext, X as X402PaymentRequired, I as HavenError, an as HavenSigningError } from './types-
|
|
1
|
+
import { Q as AgentNextStep, Z as AgentPaymentNextAction, aM as X402PaymentOption, aL as X402ExpectedContext, X as X402PaymentRequired, I as HavenError, an as HavenSigningError } from './types-Cde7k2ay.cjs';
|
|
2
2
|
import { PaymentRequirements } from 'x402/types';
|
|
3
3
|
import { Hex, Address } from 'viem';
|
|
4
4
|
|
|
@@ -200,7 +200,7 @@ declare function buildSweepAuthorizationMessage(auth: SweepAuthorization): strin
|
|
|
200
200
|
* This SDK's own version. Owned by `scripts/release-bump.mjs`, which rewrites
|
|
201
201
|
* it with the package version on every release — never hand-edit it.
|
|
202
202
|
*/
|
|
203
|
-
declare const SDK_VERSION = "0.0.0-dev.
|
|
203
|
+
declare const SDK_VERSION = "0.0.0-dev.202610151444.5ef2d94";
|
|
204
204
|
/** The request header every published Haven client sends. */
|
|
205
205
|
declare const HAVEN_CLIENT_HEADER = "X-Haven-Client";
|
|
206
206
|
/** `<package>/<version>` — the value of {@link HAVEN_CLIENT_HEADER}. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Q as AgentNextStep, Z as AgentPaymentNextAction, aM as X402PaymentOption, aL as X402ExpectedContext, X as X402PaymentRequired, I as HavenError, an as HavenSigningError } from './types-
|
|
1
|
+
import { Q as AgentNextStep, Z as AgentPaymentNextAction, aM as X402PaymentOption, aL as X402ExpectedContext, X as X402PaymentRequired, I as HavenError, an as HavenSigningError } from './types-Cde7k2ay.js';
|
|
2
2
|
import { PaymentRequirements } from 'x402/types';
|
|
3
3
|
import { Hex, Address } from 'viem';
|
|
4
4
|
|
|
@@ -200,7 +200,7 @@ declare function buildSweepAuthorizationMessage(auth: SweepAuthorization): strin
|
|
|
200
200
|
* This SDK's own version. Owned by `scripts/release-bump.mjs`, which rewrites
|
|
201
201
|
* it with the package version on every release — never hand-edit it.
|
|
202
202
|
*/
|
|
203
|
-
declare const SDK_VERSION = "0.0.0-dev.
|
|
203
|
+
declare const SDK_VERSION = "0.0.0-dev.202610151444.5ef2d94";
|
|
204
204
|
/** The request header every published Haven client sends. */
|
|
205
205
|
declare const HAVEN_CLIENT_HEADER = "X-Haven-Client";
|
|
206
206
|
/** `<package>/<version>` — the value of {@link HAVEN_CLIENT_HEADER}. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DELEGATION_MANAGER } from './chunk-
|
|
1
|
+
import { DELEGATION_MANAGER } from './chunk-T3UQTTZR.js';
|
|
2
2
|
import { ethers } from 'ethers';
|
|
3
3
|
import { keccak256, toBytes, concatHex, encodeAbiParameters } from 'viem';
|
|
4
4
|
|
|
@@ -99,5 +99,5 @@ function verifyPaymentReceipt(receipt, recover = defaultRecover) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
export { RECEIPT_VERSION, verifyPaymentReceipt };
|
|
102
|
-
//# sourceMappingURL=chunk-
|
|
103
|
-
//# sourceMappingURL=chunk-
|
|
102
|
+
//# sourceMappingURL=chunk-2BIAJVXZ.js.map
|
|
103
|
+
//# sourceMappingURL=chunk-2BIAJVXZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/receipt.ts"],"names":[],"mappings":"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","file":"chunk-OOQT7OGF.js","sourcesContent":["import { ethers } from 'ethers'\nimport { concatHex, encodeAbiParameters, keccak256, toBytes, type Hex } from 'viem'\nimport { DELEGATION_MANAGER } from './settlement-child.js'\nimport type { RawPaymentParties } from './types.js'\n\n/**\n * Verifiable payment receipts.\n *\n * A self-contained bundle for a settled Haven payment whose agent\n * authorisation can be checked offline. What `verifyPaymentReceipt` proves is\n * deliberately narrow: the `payment` block (amount, recipient) is\n * Haven-asserted and is NOT bound by the signature, and `verified: true` never\n * proves the payment settled on-chain — settlement stays an explorer check on\n * `onChain.txHash` / `settlementTxHash`.\n *\n * What `verified: true` means, per scheme:\n *\n * - erc7710 (`authorization.signatureScheme: 'eip712_delegation'`): the bundle\n * carries the settlement child's EIP-712 struct hash as `signHash`. The\n * verifier rebuilds the digest the delegate actually signed —\n * `keccak256(0x1901 ‖ domainSeparator(DelegationManager, chainId) ‖ signHash)`\n * — from the DelegationManager address the SDK pins and the bundle's\n * `onChain.chainId`, never from a value the receipt itself vouches for.\n * `verified: true` means the delegate key signed that settlement delegation\n * (`verifiedOver: 'delegation_digest'`).\n * - direct payments and the eip3009 funding leg\n * (`signatureScheme: 'eip712_userop'`) sign an ERC-4337 user operation the\n * bundle does not carry: they return `not_verifiable_offline` rather than a\n * false accusation.\n * - retired-rail bundles (no scheme) carry a raw-signed `signHash`; the\n * verifier recovers over it directly, then over the delegation digest when\n * the chain allows rebuilding it.\n *\n * Anything that is not a signed bundle — including a `haven_list_receipts`\n * history row, which carries no signature at all — returns\n * `not_a_signed_receipt`. Verification never throws.\n *\n * #3723: the response `GET /payments/{id}/receipt` returns —\n * `{ receipt, verification }` — is accepted AS-IS: when the top level carries\n * no `authorization`, the `.receipt` object inside is verified instead (one\n * level, no recursion). The wrapper's `verification` is never read — it is\n * Haven's own self-check, and trusting it would defeat an offline verifier.\n *\n * This lives in the SDK so agents and users can verify receipts client-side.\n */\nexport const RECEIPT_VERSION = 'haven-receipt-1'\n\nexport interface PaymentReceipt {\n version: typeof RECEIPT_VERSION\n paymentId: string\n payment: {\n token: string\n tokenAddress: string\n amount: string\n amountSek: string | null\n recipient: string\n /** The payer's smart-account address. */\n account: string\n /**\n * #2960: one party vocabulary for \"who paid\", additive alongside `account`\n * above (which is `parties.treasury_account` only). Optional: a server\n * from before #2960 emits neither. Ignored by `verifyPaymentReceipt`,\n * which reads only `authorization`.\n */\n parties?: RawPaymentParties\n chainId: number\n settledAt: string | null\n resourceUrl: string | null\n /**\n * #3770: the paying agent's own evidence-only verdict on what the\n * merchant DELIVERED (`ok` / `unusable` / `partial`, plus a bounded\n * note). Additive and OPTIONAL: a server from before #3770 emits\n * neither, and `null` means the agent has not reported. Ignored by\n * `verifyPaymentReceipt`, which reads only `authorization` — the same\n * rule `parties` follows. Evidence only: it never changes what the\n * payment itself is, and `settled`/amounts are untouched by a report.\n */\n deliveryQuality?: {\n quality: 'ok' | 'unusable' | 'partial'\n note: string | null\n reportedAt: string | null\n } | null\n }\n /** The agent's cryptographic authorisation — what makes the receipt verifiable. */\n authorization: {\n delegate: string\n signHash: string\n signature: string | null\n /**\n * #3418: which digest the delegate signed — selects what offline\n * verification rebuilds. `'eip712_delegation'`: the settlement\n * delegation's EIP-712 digest (erc7710). `'eip712_userop'`: an ERC-4337\n * user-operation digest the bundle does not carry (direct payments, the\n * eip3009 funding leg) — not verifiable offline. Absent on retired-rail\n * bundles and on backends older than #3418, whose `signHash` was signed\n * raw. The value is only a selector: a lying one cannot make a forged\n * signature verify, because recovery must still return `delegate`.\n */\n signatureScheme?: 'eip712_delegation' | 'eip712_userop'\n }\n onChain: {\n txHash: string | null\n chainId: number\n }\n}\n\nexport type ReceiptVerification =\n | { verified: true; recoveredSigner: string; verifiedOver: 'sign_hash' | 'delegation_digest' }\n | {\n verified: false\n reason:\n /** #3418: the input is not a haven-receipt-1 bundle (a haven_list_receipts row, null, undefined, a non-object). */\n | 'not_a_signed_receipt'\n /** #3418: eip712_userop bundle; or a delegation bundle on a chain the SDK pins no DelegationManager for. */\n | 'not_verifiable_offline'\n | 'missing_signature'\n | 'bad_signature'\n | 'signer_mismatch'\n recoveredSigner?: string\n }\n\n/** Chains whose DelegationManager the SDK pins (same address on both — rails/delegation-contracts.ts). */\nconst DELEGATION_MANAGER_CHAIN_IDS: ReadonlySet<number> = new Set([8453, 84532])\n\nconst EIP712_DOMAIN_TYPEHASH = keccak256(\n toBytes('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),\n)\n\n/**\n * The EIP-712 domain separator of the DelegationManager on `chainId`, rebuilt\n * from the manager's own constants (name/version, fixed in the contract) and\n * the address the SDK pins — never from a value the receipt vouches for.\n */\nfunction delegationDomainSeparator(chainId: number): Hex {\n return keccak256(\n encodeAbiParameters(\n [{ type: 'bytes32' }, { type: 'bytes32' }, { type: 'bytes32' }, { type: 'uint256' }, { type: 'address' }],\n [\n EIP712_DOMAIN_TYPEHASH,\n keccak256(toBytes('DelegationManager')),\n keccak256(toBytes('1')),\n BigInt(chainId),\n DELEGATION_MANAGER as Hex,\n ],\n ),\n )\n}\n\n/** The EIP-712 digest over a delegation struct hash (what the delegate signs). */\nfunction delegationDigest(structHash: string, chainId: number): Hex {\n return keccak256(concatHex(['0x1901', delegationDomainSeparator(chainId), structHash as Hex]))\n}\n\n/** Default ECDSA recovery (raw ecrecover over the hash, no message prefix). */\nfunction defaultRecover(hash: string, signature: string): string {\n return ethers.recoverAddress(hash, signature)\n}\n\nconst NOT_A_SIGNED_RECEIPT = { verified: false, reason: 'not_a_signed_receipt' } as const\n\ntype RecoverFn = (hash: string, signature: string) => string\n\ntype MatchResult =\n | { ok: true; recoveredSigner: string }\n | { ok: false; reason: 'bad_signature' | 'signer_mismatch'; recoveredSigner?: string }\n\n/** Recover over one candidate hash and compare the signer with the named delegate. */\nfunction match(delegate: string, hash: string, signature: string, recover: RecoverFn): MatchResult {\n let recovered: string\n try {\n recovered = recover(hash, signature)\n } catch {\n return { ok: false, reason: 'bad_signature' }\n }\n if (recovered.toLowerCase() !== delegate.toLowerCase()) {\n return { ok: false, reason: 'signer_mismatch', recoveredSigner: recovered }\n }\n return { ok: true, recoveredSigner: recovered }\n}\n\nfunction finish(result: MatchResult, verifiedOver: 'sign_hash' | 'delegation_digest'): ReceiptVerification {\n if (result.ok) {\n return { verified: true, recoveredSigner: result.recoveredSigner, verifiedOver }\n }\n return result.recoveredSigner !== undefined\n ? { verified: false, reason: result.reason, recoveredSigner: result.recoveredSigner }\n : { verified: false, reason: result.reason }\n}\n\n/**\n * Verify a receipt's agent authorisation offline: recover the signer and\n * confirm it is the agent delegate, over whichever digest the bundle's scheme\n * names (see the module doc for what that proves — and what it does not).\n * Never throws: anything that is not a signed receipt — a\n * `haven_list_receipts` history row, null, undefined, a non-object — returns\n * `not_a_signed_receipt`. The `{ receipt, verification }` wrapper the receipt\n * endpoint returns is accepted as-is: `.receipt` is verified, and the\n * wrapper's `verification` — Haven's own self-check — is never read. Pure —\n * `recover` is injectable but defaults to\n * standard ECDSA recovery, so this runs anywhere (no Haven backend).\n */\nexport function verifyPaymentReceipt(\n receipt: unknown,\n recover: RecoverFn = defaultRecover,\n): ReceiptVerification {\n // #3418 rule 1: a list row (or any non-bundle) has no authorization object\n // holding string delegate/signHash — answer, never throw.\n if (typeof receipt !== 'object' || receipt === null) return NOT_A_SIGNED_RECEIPT\n let source = receipt as {\n authorization?: unknown\n receipt?: unknown\n onChain?: { chainId?: unknown }\n }\n // #3723: accept the endpoint's response as-is. When the top level carries no\n // `authorization` and `.receipt` is a non-null object, verify the bundle\n // inside the `{ receipt, verification }` wrapper (GET\n // /payments/{id}/receipt, and HavenClient.getReceipt's re-wrap). ONE level\n // only — no recursion — and the wrapper's `verification` is NEVER read: it\n // is Haven's own self-check, and trusting it would defeat an offline\n // verifier. A list row wrapped or bare still answers not_a_signed_receipt.\n if (typeof source.authorization !== 'object' || source.authorization === null) {\n if (typeof source.receipt !== 'object' || source.receipt === null) {\n return NOT_A_SIGNED_RECEIPT\n }\n source = source.receipt as typeof source\n }\n if (typeof source.authorization !== 'object' || source.authorization === null) {\n return NOT_A_SIGNED_RECEIPT\n }\n const auth = source.authorization as {\n delegate?: unknown\n signHash?: unknown\n signature?: unknown\n signatureScheme?: unknown\n }\n const { delegate, signHash, signature, signatureScheme } = auth\n if (typeof delegate !== 'string' || delegate === '' || typeof signHash !== 'string' || signHash === '') {\n return NOT_A_SIGNED_RECEIPT\n }\n if (typeof signature !== 'string' || signature === '') {\n return { verified: false, reason: 'missing_signature' }\n }\n\n const chainId =\n typeof source.onChain?.chainId === 'number' ? source.onChain.chainId : undefined\n const digestVerifiable = chainId !== undefined && DELEGATION_MANAGER_CHAIN_IDS.has(chainId)\n\n // #3418 rule 4: a userop digest needs the full UserOperation, which the\n // bundle does not carry. Return not_verifiable_offline — never\n // signer_mismatch, which would accuse a genuine payment.\n if (signatureScheme === 'eip712_userop') {\n return { verified: false, reason: 'not_verifiable_offline' }\n }\n\n // #3418 rule 3: rebuild the delegation digest from the pinned manager and\n // the bundle's chain id, then recover over it.\n if (signatureScheme === 'eip712_delegation') {\n if (!digestVerifiable) {\n return { verified: false, reason: 'not_verifiable_offline' }\n }\n let digest: string\n try {\n digest = delegationDigest(signHash, chainId as number)\n } catch {\n return { verified: false, reason: 'bad_signature' }\n }\n return finish(match(delegate, digest, signature, recover), 'delegation_digest')\n }\n\n // #3418 rule 5: absent or unknown scheme — retired-rail history (signed raw\n // over signHash) or a backend older than this change. Raw recovery first,\n // then the delegation digest when the chain allows rebuilding it. This is\n // the one remaining ambiguous case, limited to old bundles.\n const raw = match(delegate, signHash, signature, recover)\n if (raw.ok) return finish(raw, 'sign_hash')\n if (digestVerifiable) {\n let digest: string\n try {\n digest = delegationDigest(signHash, chainId as number)\n } catch {\n return finish(raw, 'sign_hash')\n }\n const delegated = match(delegate, digest, signature, recover)\n if (delegated.ok) return finish(delegated, 'delegation_digest')\n }\n return finish(raw, 'sign_hash')\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/receipt.ts"],"names":[],"mappings":"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","file":"chunk-2BIAJVXZ.js","sourcesContent":["import { ethers } from 'ethers'\nimport { concatHex, encodeAbiParameters, keccak256, toBytes, type Hex } from 'viem'\nimport { DELEGATION_MANAGER } from './settlement-child.js'\nimport type { RawPaymentParties } from './types.js'\n\n/**\n * Verifiable payment receipts.\n *\n * A self-contained bundle for a settled Haven payment whose agent\n * authorisation can be checked offline. What `verifyPaymentReceipt` proves is\n * deliberately narrow: the `payment` block (amount, recipient) is\n * Haven-asserted and is NOT bound by the signature, and `verified: true` never\n * proves the payment settled on-chain — settlement stays an explorer check on\n * `onChain.txHash` / `settlementTxHash`.\n *\n * What `verified: true` means, per scheme:\n *\n * - erc7710 (`authorization.signatureScheme: 'eip712_delegation'`): the bundle\n * carries the settlement child's EIP-712 struct hash as `signHash`. The\n * verifier rebuilds the digest the delegate actually signed —\n * `keccak256(0x1901 ‖ domainSeparator(DelegationManager, chainId) ‖ signHash)`\n * — from the DelegationManager address the SDK pins and the bundle's\n * `onChain.chainId`, never from a value the receipt itself vouches for.\n * `verified: true` means the delegate key signed that settlement delegation\n * (`verifiedOver: 'delegation_digest'`).\n * - direct payments and the eip3009 funding leg\n * (`signatureScheme: 'eip712_userop'`) sign an ERC-4337 user operation the\n * bundle does not carry: they return `not_verifiable_offline` rather than a\n * false accusation.\n * - retired-rail bundles (no scheme) carry a raw-signed `signHash`; the\n * verifier recovers over it directly, then over the delegation digest when\n * the chain allows rebuilding it.\n *\n * Anything that is not a signed bundle — including a `haven_list_receipts`\n * history row, which carries no signature at all — returns\n * `not_a_signed_receipt`. Verification never throws.\n *\n * #3723: the response `GET /payments/{id}/receipt` returns —\n * `{ receipt, verification }` — is accepted AS-IS: when the top level carries\n * no `authorization`, the `.receipt` object inside is verified instead (one\n * level, no recursion). The wrapper's `verification` is never read — it is\n * Haven's own self-check, and trusting it would defeat an offline verifier.\n *\n * This lives in the SDK so agents and users can verify receipts client-side.\n */\nexport const RECEIPT_VERSION = 'haven-receipt-1'\n\nexport interface PaymentReceipt {\n version: typeof RECEIPT_VERSION\n paymentId: string\n payment: {\n token: string\n tokenAddress: string\n amount: string\n amountSek: string | null\n recipient: string\n /** The payer's smart-account address. */\n account: string\n /**\n * #2960: one party vocabulary for \"who paid\", additive alongside `account`\n * above (which is `parties.treasury_account` only). Optional: a server\n * from before #2960 emits neither. Ignored by `verifyPaymentReceipt`,\n * which reads only `authorization`.\n */\n parties?: RawPaymentParties\n chainId: number\n settledAt: string | null\n resourceUrl: string | null\n /**\n * #3770: the paying agent's own evidence-only verdict on what the\n * merchant DELIVERED (`ok` / `unusable` / `partial`, plus a bounded\n * note). Additive and OPTIONAL: a server from before #3770 emits\n * neither, and `null` means the agent has not reported. Ignored by\n * `verifyPaymentReceipt`, which reads only `authorization` — the same\n * rule `parties` follows. Evidence only: it never changes what the\n * payment itself is, and `settled`/amounts are untouched by a report.\n */\n deliveryQuality?: {\n quality: 'ok' | 'unusable' | 'partial'\n note: string | null\n reportedAt: string | null\n } | null\n }\n /** The agent's cryptographic authorisation — what makes the receipt verifiable. */\n authorization: {\n delegate: string\n signHash: string\n signature: string | null\n /**\n * #3418: which digest the delegate signed — selects what offline\n * verification rebuilds. `'eip712_delegation'`: the settlement\n * delegation's EIP-712 digest (erc7710). `'eip712_userop'`: an ERC-4337\n * user-operation digest the bundle does not carry (direct payments, the\n * eip3009 funding leg) — not verifiable offline. Absent on retired-rail\n * bundles and on backends older than #3418, whose `signHash` was signed\n * raw. The value is only a selector: a lying one cannot make a forged\n * signature verify, because recovery must still return `delegate`.\n */\n signatureScheme?: 'eip712_delegation' | 'eip712_userop'\n }\n onChain: {\n txHash: string | null\n chainId: number\n }\n}\n\nexport type ReceiptVerification =\n | { verified: true; recoveredSigner: string; verifiedOver: 'sign_hash' | 'delegation_digest' }\n | {\n verified: false\n reason:\n /** #3418: the input is not a haven-receipt-1 bundle (a haven_list_receipts row, null, undefined, a non-object). */\n | 'not_a_signed_receipt'\n /** #3418: eip712_userop bundle; or a delegation bundle on a chain the SDK pins no DelegationManager for. */\n | 'not_verifiable_offline'\n | 'missing_signature'\n | 'bad_signature'\n | 'signer_mismatch'\n recoveredSigner?: string\n }\n\n/** Chains whose DelegationManager the SDK pins (same address on both — rails/delegation-contracts.ts). */\nconst DELEGATION_MANAGER_CHAIN_IDS: ReadonlySet<number> = new Set([8453, 84532])\n\nconst EIP712_DOMAIN_TYPEHASH = keccak256(\n toBytes('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),\n)\n\n/**\n * The EIP-712 domain separator of the DelegationManager on `chainId`, rebuilt\n * from the manager's own constants (name/version, fixed in the contract) and\n * the address the SDK pins — never from a value the receipt vouches for.\n */\nfunction delegationDomainSeparator(chainId: number): Hex {\n return keccak256(\n encodeAbiParameters(\n [{ type: 'bytes32' }, { type: 'bytes32' }, { type: 'bytes32' }, { type: 'uint256' }, { type: 'address' }],\n [\n EIP712_DOMAIN_TYPEHASH,\n keccak256(toBytes('DelegationManager')),\n keccak256(toBytes('1')),\n BigInt(chainId),\n DELEGATION_MANAGER as Hex,\n ],\n ),\n )\n}\n\n/** The EIP-712 digest over a delegation struct hash (what the delegate signs). */\nfunction delegationDigest(structHash: string, chainId: number): Hex {\n return keccak256(concatHex(['0x1901', delegationDomainSeparator(chainId), structHash as Hex]))\n}\n\n/** Default ECDSA recovery (raw ecrecover over the hash, no message prefix). */\nfunction defaultRecover(hash: string, signature: string): string {\n return ethers.recoverAddress(hash, signature)\n}\n\nconst NOT_A_SIGNED_RECEIPT = { verified: false, reason: 'not_a_signed_receipt' } as const\n\ntype RecoverFn = (hash: string, signature: string) => string\n\ntype MatchResult =\n | { ok: true; recoveredSigner: string }\n | { ok: false; reason: 'bad_signature' | 'signer_mismatch'; recoveredSigner?: string }\n\n/** Recover over one candidate hash and compare the signer with the named delegate. */\nfunction match(delegate: string, hash: string, signature: string, recover: RecoverFn): MatchResult {\n let recovered: string\n try {\n recovered = recover(hash, signature)\n } catch {\n return { ok: false, reason: 'bad_signature' }\n }\n if (recovered.toLowerCase() !== delegate.toLowerCase()) {\n return { ok: false, reason: 'signer_mismatch', recoveredSigner: recovered }\n }\n return { ok: true, recoveredSigner: recovered }\n}\n\nfunction finish(result: MatchResult, verifiedOver: 'sign_hash' | 'delegation_digest'): ReceiptVerification {\n if (result.ok) {\n return { verified: true, recoveredSigner: result.recoveredSigner, verifiedOver }\n }\n return result.recoveredSigner !== undefined\n ? { verified: false, reason: result.reason, recoveredSigner: result.recoveredSigner }\n : { verified: false, reason: result.reason }\n}\n\n/**\n * Verify a receipt's agent authorisation offline: recover the signer and\n * confirm it is the agent delegate, over whichever digest the bundle's scheme\n * names (see the module doc for what that proves — and what it does not).\n * Never throws: anything that is not a signed receipt — a\n * `haven_list_receipts` history row, null, undefined, a non-object — returns\n * `not_a_signed_receipt`. The `{ receipt, verification }` wrapper the receipt\n * endpoint returns is accepted as-is: `.receipt` is verified, and the\n * wrapper's `verification` — Haven's own self-check — is never read. Pure —\n * `recover` is injectable but defaults to\n * standard ECDSA recovery, so this runs anywhere (no Haven backend).\n */\nexport function verifyPaymentReceipt(\n receipt: unknown,\n recover: RecoverFn = defaultRecover,\n): ReceiptVerification {\n // #3418 rule 1: a list row (or any non-bundle) has no authorization object\n // holding string delegate/signHash — answer, never throw.\n if (typeof receipt !== 'object' || receipt === null) return NOT_A_SIGNED_RECEIPT\n let source = receipt as {\n authorization?: unknown\n receipt?: unknown\n onChain?: { chainId?: unknown }\n }\n // #3723: accept the endpoint's response as-is. When the top level carries no\n // `authorization` and `.receipt` is a non-null object, verify the bundle\n // inside the `{ receipt, verification }` wrapper (GET\n // /payments/{id}/receipt, and HavenClient.getReceipt's re-wrap). ONE level\n // only — no recursion — and the wrapper's `verification` is NEVER read: it\n // is Haven's own self-check, and trusting it would defeat an offline\n // verifier. A list row wrapped or bare still answers not_a_signed_receipt.\n if (typeof source.authorization !== 'object' || source.authorization === null) {\n if (typeof source.receipt !== 'object' || source.receipt === null) {\n return NOT_A_SIGNED_RECEIPT\n }\n source = source.receipt as typeof source\n }\n if (typeof source.authorization !== 'object' || source.authorization === null) {\n return NOT_A_SIGNED_RECEIPT\n }\n const auth = source.authorization as {\n delegate?: unknown\n signHash?: unknown\n signature?: unknown\n signatureScheme?: unknown\n }\n const { delegate, signHash, signature, signatureScheme } = auth\n if (typeof delegate !== 'string' || delegate === '' || typeof signHash !== 'string' || signHash === '') {\n return NOT_A_SIGNED_RECEIPT\n }\n if (typeof signature !== 'string' || signature === '') {\n return { verified: false, reason: 'missing_signature' }\n }\n\n const chainId =\n typeof source.onChain?.chainId === 'number' ? source.onChain.chainId : undefined\n const digestVerifiable = chainId !== undefined && DELEGATION_MANAGER_CHAIN_IDS.has(chainId)\n\n // #3418 rule 4: a userop digest needs the full UserOperation, which the\n // bundle does not carry. Return not_verifiable_offline — never\n // signer_mismatch, which would accuse a genuine payment.\n if (signatureScheme === 'eip712_userop') {\n return { verified: false, reason: 'not_verifiable_offline' }\n }\n\n // #3418 rule 3: rebuild the delegation digest from the pinned manager and\n // the bundle's chain id, then recover over it.\n if (signatureScheme === 'eip712_delegation') {\n if (!digestVerifiable) {\n return { verified: false, reason: 'not_verifiable_offline' }\n }\n let digest: string\n try {\n digest = delegationDigest(signHash, chainId as number)\n } catch {\n return { verified: false, reason: 'bad_signature' }\n }\n return finish(match(delegate, digest, signature, recover), 'delegation_digest')\n }\n\n // #3418 rule 5: absent or unknown scheme — retired-rail history (signed raw\n // over signHash) or a backend older than this change. Raw recovery first,\n // then the delegation digest when the chain allows rebuilding it. This is\n // the one remaining ambiguous case, limited to old bundles.\n const raw = match(delegate, signHash, signature, recover)\n if (raw.ok) return finish(raw, 'sign_hash')\n if (digestVerifiable) {\n let digest: string\n try {\n digest = delegationDigest(signHash, chainId as number)\n } catch {\n return finish(raw, 'sign_hash')\n }\n const delegated = match(delegate, digest, signature, recover)\n if (delegated.ok) return finish(delegated, 'delegation_digest')\n }\n return finish(raw, 'sign_hash')\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkCM4AZGVP_cjs = require('./chunk-CM4AZGVP.cjs');
|
|
4
4
|
var ethers = require('ethers');
|
|
5
5
|
var viem = require('viem');
|
|
6
6
|
|
|
@@ -18,7 +18,7 @@ function delegationDomainSeparator(chainId) {
|
|
|
18
18
|
viem.keccak256(viem.toBytes("DelegationManager")),
|
|
19
19
|
viem.keccak256(viem.toBytes("1")),
|
|
20
20
|
BigInt(chainId),
|
|
21
|
-
|
|
21
|
+
chunkCM4AZGVP_cjs.DELEGATION_MANAGER
|
|
22
22
|
]
|
|
23
23
|
)
|
|
24
24
|
);
|
|
@@ -102,5 +102,5 @@ function verifyPaymentReceipt(receipt, recover = defaultRecover) {
|
|
|
102
102
|
|
|
103
103
|
exports.RECEIPT_VERSION = RECEIPT_VERSION;
|
|
104
104
|
exports.verifyPaymentReceipt = verifyPaymentReceipt;
|
|
105
|
-
//# sourceMappingURL=chunk-
|
|
106
|
-
//# sourceMappingURL=chunk-
|
|
105
|
+
//# sourceMappingURL=chunk-4BCOX5AL.cjs.map
|
|
106
|
+
//# sourceMappingURL=chunk-4BCOX5AL.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/receipt.ts"],"names":["keccak256","toBytes","encodeAbiParameters","DELEGATION_MANAGER","concatHex","ethers"],"mappings":"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","file":"chunk-EZA47UQ5.cjs","sourcesContent":["import { ethers } from 'ethers'\nimport { concatHex, encodeAbiParameters, keccak256, toBytes, type Hex } from 'viem'\nimport { DELEGATION_MANAGER } from './settlement-child.js'\nimport type { RawPaymentParties } from './types.js'\n\n/**\n * Verifiable payment receipts.\n *\n * A self-contained bundle for a settled Haven payment whose agent\n * authorisation can be checked offline. What `verifyPaymentReceipt` proves is\n * deliberately narrow: the `payment` block (amount, recipient) is\n * Haven-asserted and is NOT bound by the signature, and `verified: true` never\n * proves the payment settled on-chain — settlement stays an explorer check on\n * `onChain.txHash` / `settlementTxHash`.\n *\n * What `verified: true` means, per scheme:\n *\n * - erc7710 (`authorization.signatureScheme: 'eip712_delegation'`): the bundle\n * carries the settlement child's EIP-712 struct hash as `signHash`. The\n * verifier rebuilds the digest the delegate actually signed —\n * `keccak256(0x1901 ‖ domainSeparator(DelegationManager, chainId) ‖ signHash)`\n * — from the DelegationManager address the SDK pins and the bundle's\n * `onChain.chainId`, never from a value the receipt itself vouches for.\n * `verified: true` means the delegate key signed that settlement delegation\n * (`verifiedOver: 'delegation_digest'`).\n * - direct payments and the eip3009 funding leg\n * (`signatureScheme: 'eip712_userop'`) sign an ERC-4337 user operation the\n * bundle does not carry: they return `not_verifiable_offline` rather than a\n * false accusation.\n * - retired-rail bundles (no scheme) carry a raw-signed `signHash`; the\n * verifier recovers over it directly, then over the delegation digest when\n * the chain allows rebuilding it.\n *\n * Anything that is not a signed bundle — including a `haven_list_receipts`\n * history row, which carries no signature at all — returns\n * `not_a_signed_receipt`. Verification never throws.\n *\n * #3723: the response `GET /payments/{id}/receipt` returns —\n * `{ receipt, verification }` — is accepted AS-IS: when the top level carries\n * no `authorization`, the `.receipt` object inside is verified instead (one\n * level, no recursion). The wrapper's `verification` is never read — it is\n * Haven's own self-check, and trusting it would defeat an offline verifier.\n *\n * This lives in the SDK so agents and users can verify receipts client-side.\n */\nexport const RECEIPT_VERSION = 'haven-receipt-1'\n\nexport interface PaymentReceipt {\n version: typeof RECEIPT_VERSION\n paymentId: string\n payment: {\n token: string\n tokenAddress: string\n amount: string\n amountSek: string | null\n recipient: string\n /** The payer's smart-account address. */\n account: string\n /**\n * #2960: one party vocabulary for \"who paid\", additive alongside `account`\n * above (which is `parties.treasury_account` only). Optional: a server\n * from before #2960 emits neither. Ignored by `verifyPaymentReceipt`,\n * which reads only `authorization`.\n */\n parties?: RawPaymentParties\n chainId: number\n settledAt: string | null\n resourceUrl: string | null\n /**\n * #3770: the paying agent's own evidence-only verdict on what the\n * merchant DELIVERED (`ok` / `unusable` / `partial`, plus a bounded\n * note). Additive and OPTIONAL: a server from before #3770 emits\n * neither, and `null` means the agent has not reported. Ignored by\n * `verifyPaymentReceipt`, which reads only `authorization` — the same\n * rule `parties` follows. Evidence only: it never changes what the\n * payment itself is, and `settled`/amounts are untouched by a report.\n */\n deliveryQuality?: {\n quality: 'ok' | 'unusable' | 'partial'\n note: string | null\n reportedAt: string | null\n } | null\n }\n /** The agent's cryptographic authorisation — what makes the receipt verifiable. */\n authorization: {\n delegate: string\n signHash: string\n signature: string | null\n /**\n * #3418: which digest the delegate signed — selects what offline\n * verification rebuilds. `'eip712_delegation'`: the settlement\n * delegation's EIP-712 digest (erc7710). `'eip712_userop'`: an ERC-4337\n * user-operation digest the bundle does not carry (direct payments, the\n * eip3009 funding leg) — not verifiable offline. Absent on retired-rail\n * bundles and on backends older than #3418, whose `signHash` was signed\n * raw. The value is only a selector: a lying one cannot make a forged\n * signature verify, because recovery must still return `delegate`.\n */\n signatureScheme?: 'eip712_delegation' | 'eip712_userop'\n }\n onChain: {\n txHash: string | null\n chainId: number\n }\n}\n\nexport type ReceiptVerification =\n | { verified: true; recoveredSigner: string; verifiedOver: 'sign_hash' | 'delegation_digest' }\n | {\n verified: false\n reason:\n /** #3418: the input is not a haven-receipt-1 bundle (a haven_list_receipts row, null, undefined, a non-object). */\n | 'not_a_signed_receipt'\n /** #3418: eip712_userop bundle; or a delegation bundle on a chain the SDK pins no DelegationManager for. */\n | 'not_verifiable_offline'\n | 'missing_signature'\n | 'bad_signature'\n | 'signer_mismatch'\n recoveredSigner?: string\n }\n\n/** Chains whose DelegationManager the SDK pins (same address on both — rails/delegation-contracts.ts). */\nconst DELEGATION_MANAGER_CHAIN_IDS: ReadonlySet<number> = new Set([8453, 84532])\n\nconst EIP712_DOMAIN_TYPEHASH = keccak256(\n toBytes('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),\n)\n\n/**\n * The EIP-712 domain separator of the DelegationManager on `chainId`, rebuilt\n * from the manager's own constants (name/version, fixed in the contract) and\n * the address the SDK pins — never from a value the receipt vouches for.\n */\nfunction delegationDomainSeparator(chainId: number): Hex {\n return keccak256(\n encodeAbiParameters(\n [{ type: 'bytes32' }, { type: 'bytes32' }, { type: 'bytes32' }, { type: 'uint256' }, { type: 'address' }],\n [\n EIP712_DOMAIN_TYPEHASH,\n keccak256(toBytes('DelegationManager')),\n keccak256(toBytes('1')),\n BigInt(chainId),\n DELEGATION_MANAGER as Hex,\n ],\n ),\n )\n}\n\n/** The EIP-712 digest over a delegation struct hash (what the delegate signs). */\nfunction delegationDigest(structHash: string, chainId: number): Hex {\n return keccak256(concatHex(['0x1901', delegationDomainSeparator(chainId), structHash as Hex]))\n}\n\n/** Default ECDSA recovery (raw ecrecover over the hash, no message prefix). */\nfunction defaultRecover(hash: string, signature: string): string {\n return ethers.recoverAddress(hash, signature)\n}\n\nconst NOT_A_SIGNED_RECEIPT = { verified: false, reason: 'not_a_signed_receipt' } as const\n\ntype RecoverFn = (hash: string, signature: string) => string\n\ntype MatchResult =\n | { ok: true; recoveredSigner: string }\n | { ok: false; reason: 'bad_signature' | 'signer_mismatch'; recoveredSigner?: string }\n\n/** Recover over one candidate hash and compare the signer with the named delegate. */\nfunction match(delegate: string, hash: string, signature: string, recover: RecoverFn): MatchResult {\n let recovered: string\n try {\n recovered = recover(hash, signature)\n } catch {\n return { ok: false, reason: 'bad_signature' }\n }\n if (recovered.toLowerCase() !== delegate.toLowerCase()) {\n return { ok: false, reason: 'signer_mismatch', recoveredSigner: recovered }\n }\n return { ok: true, recoveredSigner: recovered }\n}\n\nfunction finish(result: MatchResult, verifiedOver: 'sign_hash' | 'delegation_digest'): ReceiptVerification {\n if (result.ok) {\n return { verified: true, recoveredSigner: result.recoveredSigner, verifiedOver }\n }\n return result.recoveredSigner !== undefined\n ? { verified: false, reason: result.reason, recoveredSigner: result.recoveredSigner }\n : { verified: false, reason: result.reason }\n}\n\n/**\n * Verify a receipt's agent authorisation offline: recover the signer and\n * confirm it is the agent delegate, over whichever digest the bundle's scheme\n * names (see the module doc for what that proves — and what it does not).\n * Never throws: anything that is not a signed receipt — a\n * `haven_list_receipts` history row, null, undefined, a non-object — returns\n * `not_a_signed_receipt`. The `{ receipt, verification }` wrapper the receipt\n * endpoint returns is accepted as-is: `.receipt` is verified, and the\n * wrapper's `verification` — Haven's own self-check — is never read. Pure —\n * `recover` is injectable but defaults to\n * standard ECDSA recovery, so this runs anywhere (no Haven backend).\n */\nexport function verifyPaymentReceipt(\n receipt: unknown,\n recover: RecoverFn = defaultRecover,\n): ReceiptVerification {\n // #3418 rule 1: a list row (or any non-bundle) has no authorization object\n // holding string delegate/signHash — answer, never throw.\n if (typeof receipt !== 'object' || receipt === null) return NOT_A_SIGNED_RECEIPT\n let source = receipt as {\n authorization?: unknown\n receipt?: unknown\n onChain?: { chainId?: unknown }\n }\n // #3723: accept the endpoint's response as-is. When the top level carries no\n // `authorization` and `.receipt` is a non-null object, verify the bundle\n // inside the `{ receipt, verification }` wrapper (GET\n // /payments/{id}/receipt, and HavenClient.getReceipt's re-wrap). ONE level\n // only — no recursion — and the wrapper's `verification` is NEVER read: it\n // is Haven's own self-check, and trusting it would defeat an offline\n // verifier. A list row wrapped or bare still answers not_a_signed_receipt.\n if (typeof source.authorization !== 'object' || source.authorization === null) {\n if (typeof source.receipt !== 'object' || source.receipt === null) {\n return NOT_A_SIGNED_RECEIPT\n }\n source = source.receipt as typeof source\n }\n if (typeof source.authorization !== 'object' || source.authorization === null) {\n return NOT_A_SIGNED_RECEIPT\n }\n const auth = source.authorization as {\n delegate?: unknown\n signHash?: unknown\n signature?: unknown\n signatureScheme?: unknown\n }\n const { delegate, signHash, signature, signatureScheme } = auth\n if (typeof delegate !== 'string' || delegate === '' || typeof signHash !== 'string' || signHash === '') {\n return NOT_A_SIGNED_RECEIPT\n }\n if (typeof signature !== 'string' || signature === '') {\n return { verified: false, reason: 'missing_signature' }\n }\n\n const chainId =\n typeof source.onChain?.chainId === 'number' ? source.onChain.chainId : undefined\n const digestVerifiable = chainId !== undefined && DELEGATION_MANAGER_CHAIN_IDS.has(chainId)\n\n // #3418 rule 4: a userop digest needs the full UserOperation, which the\n // bundle does not carry. Return not_verifiable_offline — never\n // signer_mismatch, which would accuse a genuine payment.\n if (signatureScheme === 'eip712_userop') {\n return { verified: false, reason: 'not_verifiable_offline' }\n }\n\n // #3418 rule 3: rebuild the delegation digest from the pinned manager and\n // the bundle's chain id, then recover over it.\n if (signatureScheme === 'eip712_delegation') {\n if (!digestVerifiable) {\n return { verified: false, reason: 'not_verifiable_offline' }\n }\n let digest: string\n try {\n digest = delegationDigest(signHash, chainId as number)\n } catch {\n return { verified: false, reason: 'bad_signature' }\n }\n return finish(match(delegate, digest, signature, recover), 'delegation_digest')\n }\n\n // #3418 rule 5: absent or unknown scheme — retired-rail history (signed raw\n // over signHash) or a backend older than this change. Raw recovery first,\n // then the delegation digest when the chain allows rebuilding it. This is\n // the one remaining ambiguous case, limited to old bundles.\n const raw = match(delegate, signHash, signature, recover)\n if (raw.ok) return finish(raw, 'sign_hash')\n if (digestVerifiable) {\n let digest: string\n try {\n digest = delegationDigest(signHash, chainId as number)\n } catch {\n return finish(raw, 'sign_hash')\n }\n const delegated = match(delegate, digest, signature, recover)\n if (delegated.ok) return finish(delegated, 'delegation_digest')\n }\n return finish(raw, 'sign_hash')\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/receipt.ts"],"names":["keccak256","toBytes","encodeAbiParameters","DELEGATION_MANAGER","concatHex","ethers"],"mappings":"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","file":"chunk-4BCOX5AL.cjs","sourcesContent":["import { ethers } from 'ethers'\nimport { concatHex, encodeAbiParameters, keccak256, toBytes, type Hex } from 'viem'\nimport { DELEGATION_MANAGER } from './settlement-child.js'\nimport type { RawPaymentParties } from './types.js'\n\n/**\n * Verifiable payment receipts.\n *\n * A self-contained bundle for a settled Haven payment whose agent\n * authorisation can be checked offline. What `verifyPaymentReceipt` proves is\n * deliberately narrow: the `payment` block (amount, recipient) is\n * Haven-asserted and is NOT bound by the signature, and `verified: true` never\n * proves the payment settled on-chain — settlement stays an explorer check on\n * `onChain.txHash` / `settlementTxHash`.\n *\n * What `verified: true` means, per scheme:\n *\n * - erc7710 (`authorization.signatureScheme: 'eip712_delegation'`): the bundle\n * carries the settlement child's EIP-712 struct hash as `signHash`. The\n * verifier rebuilds the digest the delegate actually signed —\n * `keccak256(0x1901 ‖ domainSeparator(DelegationManager, chainId) ‖ signHash)`\n * — from the DelegationManager address the SDK pins and the bundle's\n * `onChain.chainId`, never from a value the receipt itself vouches for.\n * `verified: true` means the delegate key signed that settlement delegation\n * (`verifiedOver: 'delegation_digest'`).\n * - direct payments and the eip3009 funding leg\n * (`signatureScheme: 'eip712_userop'`) sign an ERC-4337 user operation the\n * bundle does not carry: they return `not_verifiable_offline` rather than a\n * false accusation.\n * - retired-rail bundles (no scheme) carry a raw-signed `signHash`; the\n * verifier recovers over it directly, then over the delegation digest when\n * the chain allows rebuilding it.\n *\n * Anything that is not a signed bundle — including a `haven_list_receipts`\n * history row, which carries no signature at all — returns\n * `not_a_signed_receipt`. Verification never throws.\n *\n * #3723: the response `GET /payments/{id}/receipt` returns —\n * `{ receipt, verification }` — is accepted AS-IS: when the top level carries\n * no `authorization`, the `.receipt` object inside is verified instead (one\n * level, no recursion). The wrapper's `verification` is never read — it is\n * Haven's own self-check, and trusting it would defeat an offline verifier.\n *\n * This lives in the SDK so agents and users can verify receipts client-side.\n */\nexport const RECEIPT_VERSION = 'haven-receipt-1'\n\nexport interface PaymentReceipt {\n version: typeof RECEIPT_VERSION\n paymentId: string\n payment: {\n token: string\n tokenAddress: string\n amount: string\n amountSek: string | null\n recipient: string\n /** The payer's smart-account address. */\n account: string\n /**\n * #2960: one party vocabulary for \"who paid\", additive alongside `account`\n * above (which is `parties.treasury_account` only). Optional: a server\n * from before #2960 emits neither. Ignored by `verifyPaymentReceipt`,\n * which reads only `authorization`.\n */\n parties?: RawPaymentParties\n chainId: number\n settledAt: string | null\n resourceUrl: string | null\n /**\n * #3770: the paying agent's own evidence-only verdict on what the\n * merchant DELIVERED (`ok` / `unusable` / `partial`, plus a bounded\n * note). Additive and OPTIONAL: a server from before #3770 emits\n * neither, and `null` means the agent has not reported. Ignored by\n * `verifyPaymentReceipt`, which reads only `authorization` — the same\n * rule `parties` follows. Evidence only: it never changes what the\n * payment itself is, and `settled`/amounts are untouched by a report.\n */\n deliveryQuality?: {\n quality: 'ok' | 'unusable' | 'partial'\n note: string | null\n reportedAt: string | null\n } | null\n }\n /** The agent's cryptographic authorisation — what makes the receipt verifiable. */\n authorization: {\n delegate: string\n signHash: string\n signature: string | null\n /**\n * #3418: which digest the delegate signed — selects what offline\n * verification rebuilds. `'eip712_delegation'`: the settlement\n * delegation's EIP-712 digest (erc7710). `'eip712_userop'`: an ERC-4337\n * user-operation digest the bundle does not carry (direct payments, the\n * eip3009 funding leg) — not verifiable offline. Absent on retired-rail\n * bundles and on backends older than #3418, whose `signHash` was signed\n * raw. The value is only a selector: a lying one cannot make a forged\n * signature verify, because recovery must still return `delegate`.\n */\n signatureScheme?: 'eip712_delegation' | 'eip712_userop'\n }\n onChain: {\n txHash: string | null\n chainId: number\n }\n}\n\nexport type ReceiptVerification =\n | { verified: true; recoveredSigner: string; verifiedOver: 'sign_hash' | 'delegation_digest' }\n | {\n verified: false\n reason:\n /** #3418: the input is not a haven-receipt-1 bundle (a haven_list_receipts row, null, undefined, a non-object). */\n | 'not_a_signed_receipt'\n /** #3418: eip712_userop bundle; or a delegation bundle on a chain the SDK pins no DelegationManager for. */\n | 'not_verifiable_offline'\n | 'missing_signature'\n | 'bad_signature'\n | 'signer_mismatch'\n recoveredSigner?: string\n }\n\n/** Chains whose DelegationManager the SDK pins (same address on both — rails/delegation-contracts.ts). */\nconst DELEGATION_MANAGER_CHAIN_IDS: ReadonlySet<number> = new Set([8453, 84532])\n\nconst EIP712_DOMAIN_TYPEHASH = keccak256(\n toBytes('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),\n)\n\n/**\n * The EIP-712 domain separator of the DelegationManager on `chainId`, rebuilt\n * from the manager's own constants (name/version, fixed in the contract) and\n * the address the SDK pins — never from a value the receipt vouches for.\n */\nfunction delegationDomainSeparator(chainId: number): Hex {\n return keccak256(\n encodeAbiParameters(\n [{ type: 'bytes32' }, { type: 'bytes32' }, { type: 'bytes32' }, { type: 'uint256' }, { type: 'address' }],\n [\n EIP712_DOMAIN_TYPEHASH,\n keccak256(toBytes('DelegationManager')),\n keccak256(toBytes('1')),\n BigInt(chainId),\n DELEGATION_MANAGER as Hex,\n ],\n ),\n )\n}\n\n/** The EIP-712 digest over a delegation struct hash (what the delegate signs). */\nfunction delegationDigest(structHash: string, chainId: number): Hex {\n return keccak256(concatHex(['0x1901', delegationDomainSeparator(chainId), structHash as Hex]))\n}\n\n/** Default ECDSA recovery (raw ecrecover over the hash, no message prefix). */\nfunction defaultRecover(hash: string, signature: string): string {\n return ethers.recoverAddress(hash, signature)\n}\n\nconst NOT_A_SIGNED_RECEIPT = { verified: false, reason: 'not_a_signed_receipt' } as const\n\ntype RecoverFn = (hash: string, signature: string) => string\n\ntype MatchResult =\n | { ok: true; recoveredSigner: string }\n | { ok: false; reason: 'bad_signature' | 'signer_mismatch'; recoveredSigner?: string }\n\n/** Recover over one candidate hash and compare the signer with the named delegate. */\nfunction match(delegate: string, hash: string, signature: string, recover: RecoverFn): MatchResult {\n let recovered: string\n try {\n recovered = recover(hash, signature)\n } catch {\n return { ok: false, reason: 'bad_signature' }\n }\n if (recovered.toLowerCase() !== delegate.toLowerCase()) {\n return { ok: false, reason: 'signer_mismatch', recoveredSigner: recovered }\n }\n return { ok: true, recoveredSigner: recovered }\n}\n\nfunction finish(result: MatchResult, verifiedOver: 'sign_hash' | 'delegation_digest'): ReceiptVerification {\n if (result.ok) {\n return { verified: true, recoveredSigner: result.recoveredSigner, verifiedOver }\n }\n return result.recoveredSigner !== undefined\n ? { verified: false, reason: result.reason, recoveredSigner: result.recoveredSigner }\n : { verified: false, reason: result.reason }\n}\n\n/**\n * Verify a receipt's agent authorisation offline: recover the signer and\n * confirm it is the agent delegate, over whichever digest the bundle's scheme\n * names (see the module doc for what that proves — and what it does not).\n * Never throws: anything that is not a signed receipt — a\n * `haven_list_receipts` history row, null, undefined, a non-object — returns\n * `not_a_signed_receipt`. The `{ receipt, verification }` wrapper the receipt\n * endpoint returns is accepted as-is: `.receipt` is verified, and the\n * wrapper's `verification` — Haven's own self-check — is never read. Pure —\n * `recover` is injectable but defaults to\n * standard ECDSA recovery, so this runs anywhere (no Haven backend).\n */\nexport function verifyPaymentReceipt(\n receipt: unknown,\n recover: RecoverFn = defaultRecover,\n): ReceiptVerification {\n // #3418 rule 1: a list row (or any non-bundle) has no authorization object\n // holding string delegate/signHash — answer, never throw.\n if (typeof receipt !== 'object' || receipt === null) return NOT_A_SIGNED_RECEIPT\n let source = receipt as {\n authorization?: unknown\n receipt?: unknown\n onChain?: { chainId?: unknown }\n }\n // #3723: accept the endpoint's response as-is. When the top level carries no\n // `authorization` and `.receipt` is a non-null object, verify the bundle\n // inside the `{ receipt, verification }` wrapper (GET\n // /payments/{id}/receipt, and HavenClient.getReceipt's re-wrap). ONE level\n // only — no recursion — and the wrapper's `verification` is NEVER read: it\n // is Haven's own self-check, and trusting it would defeat an offline\n // verifier. A list row wrapped or bare still answers not_a_signed_receipt.\n if (typeof source.authorization !== 'object' || source.authorization === null) {\n if (typeof source.receipt !== 'object' || source.receipt === null) {\n return NOT_A_SIGNED_RECEIPT\n }\n source = source.receipt as typeof source\n }\n if (typeof source.authorization !== 'object' || source.authorization === null) {\n return NOT_A_SIGNED_RECEIPT\n }\n const auth = source.authorization as {\n delegate?: unknown\n signHash?: unknown\n signature?: unknown\n signatureScheme?: unknown\n }\n const { delegate, signHash, signature, signatureScheme } = auth\n if (typeof delegate !== 'string' || delegate === '' || typeof signHash !== 'string' || signHash === '') {\n return NOT_A_SIGNED_RECEIPT\n }\n if (typeof signature !== 'string' || signature === '') {\n return { verified: false, reason: 'missing_signature' }\n }\n\n const chainId =\n typeof source.onChain?.chainId === 'number' ? source.onChain.chainId : undefined\n const digestVerifiable = chainId !== undefined && DELEGATION_MANAGER_CHAIN_IDS.has(chainId)\n\n // #3418 rule 4: a userop digest needs the full UserOperation, which the\n // bundle does not carry. Return not_verifiable_offline — never\n // signer_mismatch, which would accuse a genuine payment.\n if (signatureScheme === 'eip712_userop') {\n return { verified: false, reason: 'not_verifiable_offline' }\n }\n\n // #3418 rule 3: rebuild the delegation digest from the pinned manager and\n // the bundle's chain id, then recover over it.\n if (signatureScheme === 'eip712_delegation') {\n if (!digestVerifiable) {\n return { verified: false, reason: 'not_verifiable_offline' }\n }\n let digest: string\n try {\n digest = delegationDigest(signHash, chainId as number)\n } catch {\n return { verified: false, reason: 'bad_signature' }\n }\n return finish(match(delegate, digest, signature, recover), 'delegation_digest')\n }\n\n // #3418 rule 5: absent or unknown scheme — retired-rail history (signed raw\n // over signHash) or a backend older than this change. Raw recovery first,\n // then the delegation digest when the chain allows rebuilding it. This is\n // the one remaining ambiguous case, limited to old bundles.\n const raw = match(delegate, signHash, signature, recover)\n if (raw.ok) return finish(raw, 'sign_hash')\n if (digestVerifiable) {\n let digest: string\n try {\n digest = delegationDigest(signHash, chainId as number)\n } catch {\n return finish(raw, 'sign_hash')\n }\n const delegated = match(delegate, digest, signature, recover)\n if (delegated.ok) return finish(delegated, 'delegation_digest')\n }\n return finish(raw, 'sign_hash')\n}\n"]}
|
|
@@ -314,7 +314,23 @@ var AgentPaymentWarningCode = {
|
|
|
314
314
|
* money moved". Carries the intent's `expires_at`: after that instant the
|
|
315
315
|
* settlement can no longer land at all.
|
|
316
316
|
*/
|
|
317
|
-
SettlementUnconfirmed: "SETTLEMENT_UNCONFIRMED"
|
|
317
|
+
SettlementUnconfirmed: "SETTLEMENT_UNCONFIRMED",
|
|
318
|
+
/**
|
|
319
|
+
* #3967: the quote-time budget pre-check decided the quoted amount does NOT
|
|
320
|
+
* fit the agent's remaining delegation budget — the same server-side compare
|
|
321
|
+
* the payment path runs before funding. Travels beside
|
|
322
|
+
* `budget_check.sufficient: false` and, on `haven_quote_x402`,
|
|
323
|
+
* `safe_to_continue: false` with no prefilled cap. The on-chain policy
|
|
324
|
+
* remains the actual gate either way.
|
|
325
|
+
*/
|
|
326
|
+
QuoteExceedsRemainingBudget: "QUOTE_EXCEEDS_REMAINING_BUDGET",
|
|
327
|
+
/**
|
|
328
|
+
* #3967: the MERCHANT reported a denial on one of its x402 payment options —
|
|
329
|
+
* a known denial key (starting with `issuanceDenied`) in `accepts[].extra`.
|
|
330
|
+
* Strictly informational: it flips neither `safe_to_continue` nor
|
|
331
|
+
* `expected_settleable`; the agent should surface it to the user.
|
|
332
|
+
*/
|
|
333
|
+
MerchantReportedDenial: "MERCHANT_REPORTED_DENIAL"
|
|
318
334
|
};
|
|
319
335
|
var AgentPaymentRailDescriptions = {
|
|
320
336
|
[AgentPaymentRail.Direct]: "Standard Haven payment from the user-controlled account, redeeming the agent's on-chain budget delegation.",
|
|
@@ -1083,5 +1099,5 @@ exports.packedUserOperationHash = packedUserOperationHash;
|
|
|
1083
1099
|
exports.resolveConnectorChannel = resolveConnectorChannel;
|
|
1084
1100
|
exports.signerUpdateFallback = signerUpdateFallback;
|
|
1085
1101
|
exports.verifySettlementChild = verifySettlementChild;
|
|
1086
|
-
//# sourceMappingURL=chunk-
|
|
1087
|
-
//# sourceMappingURL=chunk-
|
|
1102
|
+
//# sourceMappingURL=chunk-CM4AZGVP.cjs.map
|
|
1103
|
+
//# sourceMappingURL=chunk-CM4AZGVP.cjs.map
|