@integraledger/lcp-binding-evm-escrow 0.10.1 → 0.12.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/CHANGELOG.md +57 -0
- package/README.md +8 -0
- package/dist/manifest.d.ts +10 -0
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +11 -1
- package/dist/manifest.js.map +1 -1
- package/package.json +4 -4
- package/src/manifest.ts +11 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# @integraledger/lcp-binding-evm-escrow
|
|
2
2
|
|
|
3
|
+
## 0.12.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @integraledger/lcp-binding-core@0.12.0
|
|
8
|
+
- @integraledger/lcp-binding-evm-common@0.12.0
|
|
9
|
+
|
|
10
|
+
## 0.10.2
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- b2ffecc: Report the class a record actually supports, place a terms URL on UCP, and stop refusing a conformant UCP
|
|
15
|
+
profile — the remediation of the 2026-08-19 conformance re-audit.
|
|
16
|
+
|
|
17
|
+
**`verify` now computes `supportedClass` instead of echoing the claim.** It was `anyFailed ? "TC-0" :
|
|
18
|
+
claimedClass`, so a record proving nothing — no settlement, no acceptance, no authority chain — reported
|
|
19
|
+
whatever class the caller named, while the field's own published docblock promised "what the record
|
|
20
|
+
honestly supports, not what the caller asked for". It is now the highest class every one of whose required
|
|
21
|
+
steps is `proved`, `TC-0` on any failure, computed from the steps alone: neither capped by the claim (rungs
|
|
22
|
+
that reach TC-3 read TC-3 where the caller claimed TC-2) nor lifted by it. White paper #4 §5 defines the
|
|
23
|
+
class of a transaction as "the highest class whose criteria it fully meets", and this is that.
|
|
24
|
+
|
|
25
|
+
The claim is not discarded — the report gains **`claimedClass`**, a required member, because `verified`
|
|
26
|
+
answers "did the record reach the class it claimed?" and cannot be read without it. The two fields are the
|
|
27
|
+
report's two halves: an input echoed, and a finding computed. Where they differ, the record did not reach
|
|
28
|
+
its own shape. An out-of-taxonomy claim now lands only in the echo and can no longer masquerade as a
|
|
29
|
+
finding.
|
|
30
|
+
|
|
31
|
+
**UCP can advertise a terms URL.** Its policy object declares `url` — "Optional link to the full policy
|
|
32
|
+
document", `format: uri` — on the very entry this placement writes, and §C.3's illustration carries `url`
|
|
33
|
+
and `atrHash` side by side there. The manifest previously said the protocol had no slot, citing `links[]`,
|
|
34
|
+
which §C.3 separates as "a standing page, not a per-transaction record". The obstacle was mechanical:
|
|
35
|
+
`termsUrlFields` addresses document paths, and a tagged-array entry's index is chosen at write time. The
|
|
36
|
+
`tagged-array` container therefore gains `termsUrlField`, written onto the same entry in the same write, and
|
|
37
|
+
read back through the same first-match rule. UCP was the last shipped protocol that refused an
|
|
38
|
+
advertisement carrying its own locator (integra-protocol#8).
|
|
39
|
+
|
|
40
|
+
**`readUcpProfile` no longer refuses a conformant business profile.** `requireHttps` mapped an ABSENT `spec`
|
|
41
|
+
to the same branch as a malformed one, and the live host requires `spec` only of a platform declaration —
|
|
42
|
+
as this repository's own README already said. It is now `requireHttpsIfDeclared`: absence is absence, and a
|
|
43
|
+
declared value is still held to the host's https MUST.
|
|
44
|
+
|
|
45
|
+
Also: `requireWritten` replaces an unchecked cast in the x402 override, so a broken postcondition throws
|
|
46
|
+
instead of returning a success carrying no document; six x402 citations move to the revision that actually
|
|
47
|
+
touches the file they name, and a new gate refuses any `owner/repo@sha` in source that `spec-pins.json` does
|
|
48
|
+
not record; four spec citations move from line anchors to section anchors; the escrow binding states why it
|
|
49
|
+
declares no §8.3.1 off-canonical variant, and asserts it; and §C.3's `policies[]` illustration is recorded as
|
|
50
|
+
invalid against the live UCP schema, which shows `description` as a bare string where the host requires an
|
|
51
|
+
object — owed upstream, not a defect here.
|
|
52
|
+
|
|
53
|
+
Corpus 844 → 847, root `ec4ad1b02a81538b…`.
|
|
54
|
+
|
|
55
|
+
- Updated dependencies [b2ffecc]
|
|
56
|
+
- Updated dependencies [822190a]
|
|
57
|
+
- @integraledger/lcp-binding-core@0.11.0
|
|
58
|
+
- @integraledger/lcp-binding-evm-common@0.10.2
|
|
59
|
+
|
|
3
60
|
## 0.10.1
|
|
4
61
|
|
|
5
62
|
**0.10.0 was staged and withdrawn before approval; this is that release, re-cut.** The conformance corpus
|
package/README.md
CHANGED
|
@@ -48,6 +48,14 @@ atrHashFromSalt(0n); // the inverse, for a salt you already hold
|
|
|
48
48
|
The ATR hash rides `PaymentInfo.salt` — an existing field the service already controls. No derivation,
|
|
49
49
|
no overlay contract.
|
|
50
50
|
|
|
51
|
+
**The ATR hash must be per-transaction.** `salt` is the struct's entropy source — *"a source of entropy to
|
|
52
|
+
ensure unique hashes across different payments"* — and an ATR hash carries none: it is deterministic and
|
|
53
|
+
identical for every payment made under one terms document. The escrow keys its state on the hash of the
|
|
54
|
+
whole `PaymentInfo` and refuses one it has already collected, so a repeat purchase under the same ATR with
|
|
55
|
+
the same payer, caps and expiries reverts on chain with `PaymentAlreadyCollected`. Nothing in this package
|
|
56
|
+
can detect that — the collision is between two transactions it never sees together. Mint a fresh ATR per
|
|
57
|
+
transaction, which LCP §6.1 wants anyway.
|
|
58
|
+
|
|
51
59
|
```ts
|
|
52
60
|
import { EVENT_TO_STATE } from "@integraledger/lcp-binding-evm-escrow";
|
|
53
61
|
|
package/dist/manifest.d.ts
CHANGED
|
@@ -8,6 +8,16 @@ import type { BindingManifest } from "@integraledger/lcp-binding-core";
|
|
|
8
8
|
* in `PaymentAuthorized`/`PaymentCharged` event data, so `recover`/`enumerate` decode the event and read
|
|
9
9
|
* `salt` (proven on-chain).
|
|
10
10
|
*
|
|
11
|
+
* **NO `offCanonical`, and that silence is examined rather than default.** §8.3.1 admits the marker only
|
|
12
|
+
* where the field is "specified as client-chosen or as a deterministic derivation that excludes
|
|
13
|
+
* `atrHash`", and `salt` meets NEITHER disjunct: `charge`/`authorize` are
|
|
14
|
+
* `onlySender(paymentInfo.operator)`, so the SERVICE constructs the whole struct and the payer signs only
|
|
15
|
+
* a hash of it, and the welded value IS `uint256(atrHash)` — a derivation that includes it. Nothing
|
|
16
|
+
* on-chain constrains the field's value, which is §8.3.1's Tier A condition. The x402 sibling declares
|
|
17
|
+
* `offCanonical` over the EIP-3009 nonce because THAT field is payer-chosen and its weld therefore needs
|
|
18
|
+
* a cooperating client stack; this one needs none, so mirroring the sibling would publish a
|
|
19
|
+
* controlled-client caveat that does not apply here.
|
|
20
|
+
*
|
|
11
21
|
* **recovery.forwardIndexable = false** — the criterion is enumeration bound to a GIVEN atrHash, and this
|
|
12
22
|
* rail cannot do it. `paymentInfoHash` is the only indexed topic, and it is a hash of the whole
|
|
13
23
|
* `PaymentInfo` struct: a caller holding the complete struct could topic-filter, but a caller holding only
|
package/dist/manifest.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAEvE
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,eAAO,MAAM,eAAe,EAAE,eA+B7B,CAAC"}
|
package/dist/manifest.js
CHANGED
|
@@ -7,6 +7,16 @@
|
|
|
7
7
|
* in `PaymentAuthorized`/`PaymentCharged` event data, so `recover`/`enumerate` decode the event and read
|
|
8
8
|
* `salt` (proven on-chain).
|
|
9
9
|
*
|
|
10
|
+
* **NO `offCanonical`, and that silence is examined rather than default.** §8.3.1 admits the marker only
|
|
11
|
+
* where the field is "specified as client-chosen or as a deterministic derivation that excludes
|
|
12
|
+
* `atrHash`", and `salt` meets NEITHER disjunct: `charge`/`authorize` are
|
|
13
|
+
* `onlySender(paymentInfo.operator)`, so the SERVICE constructs the whole struct and the payer signs only
|
|
14
|
+
* a hash of it, and the welded value IS `uint256(atrHash)` — a derivation that includes it. Nothing
|
|
15
|
+
* on-chain constrains the field's value, which is §8.3.1's Tier A condition. The x402 sibling declares
|
|
16
|
+
* `offCanonical` over the EIP-3009 nonce because THAT field is payer-chosen and its weld therefore needs
|
|
17
|
+
* a cooperating client stack; this one needs none, so mirroring the sibling would publish a
|
|
18
|
+
* controlled-client caveat that does not apply here.
|
|
19
|
+
*
|
|
10
20
|
* **recovery.forwardIndexable = false** — the criterion is enumeration bound to a GIVEN atrHash, and this
|
|
11
21
|
* rail cannot do it. `paymentInfoHash` is the only indexed topic, and it is a hash of the whole
|
|
12
22
|
* `PaymentInfo` struct: a caller holding the complete struct could topic-filter, but a caller holding only
|
|
@@ -61,7 +71,7 @@ export const ESCROW_MANIFEST = {
|
|
|
61
71
|
indexing: "event-data-scan:paymentInfoHash",
|
|
62
72
|
finality: {
|
|
63
73
|
reversible: true,
|
|
64
|
-
note: 'on-rail void/refund within refundExpiry — not dispute resolution (RCS-5); capture is not reversed, refund is a fresh on-rail remedy The atrHash welded into PaymentInfo.salt MUST be per-transaction: salt is specified as the struct\'s entropy source ("a source of entropy to ensure unique hashes across different payments") and an atrHash carries none, so a repeat purchase under one ATR with the same payer, caps and expiries produces a PaymentInfo the escrow has already seen and reverts.',
|
|
74
|
+
note: 'on-rail void/refund within refundExpiry — not dispute resolution (RCS-5); capture is not reversed, refund is a fresh on-rail remedy. The atrHash welded into PaymentInfo.salt MUST be per-transaction: salt is specified as the struct\'s entropy source ("a source of entropy to ensure unique hashes across different payments") and an atrHash carries none, so a repeat purchase under one ATR with the same payer, caps and expiries produces a PaymentInfo the escrow has already seen and reverts.',
|
|
65
75
|
},
|
|
66
76
|
weldGrades: {
|
|
67
77
|
ERC3009: "signature",
|
package/dist/manifest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,MAAM,CAAC,MAAM,eAAe,GAAoB;IAC9C,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,cAAc;IACvB,WAAW,EAAE,kBAAkB;IAC/B,QAAQ,EAAE;QACR,OAAO,EAAE,IAAI;QACb,oBAAoB,EAAE,IAAI;QAC1B,gBAAgB,EAAE,KAAK;KACxB;IACD,YAAY,EAAE,SAAS,EAAE,uFAAuF;IAChH,WAAW,EAAE,YAAY,EAAE,qEAAqE;IAChG,QAAQ,EAAE,iCAAiC;IAC3C,QAAQ,EAAE;QACR,UAAU,EAAE,IAAI;QAChB,IAAI,EAAE,2eAA2e;KAClf;IACD,UAAU,EAAE;QACV,OAAO,EAAE,WAAW;QACpB,OAAO,EAAE,WAAW;QACpB,eAAe,EAAE,WAAW;QAC5B,WAAW,EAAE,IAAI;KAClB;IACD,eAAe,EAAE;QACf,UAAU;QACV,YAAY;QACZ,UAAU;QACV,SAAS;QACT,QAAQ;QACR,WAAW;QACX,UAAU;KACX;CACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@integraledger/lcp-binding-evm-escrow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Welds an ATR hash into an authorize-and-capture escrow settlement on EVM chains.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lcp",
|
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
"homepage": "https://github.com/IntegraLedger/integra-protocol/tree/main/packages/binding-evm-escrow#readme",
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"viem": "2.55.11",
|
|
46
|
-
"@integraledger/lcp-binding-
|
|
47
|
-
"@integraledger/lcp-binding-
|
|
46
|
+
"@integraledger/lcp-binding-core": "0.12.0",
|
|
47
|
+
"@integraledger/lcp-binding-evm-common": "0.12.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/node": "24.13.3",
|
|
51
51
|
"vitest": "4.1.10",
|
|
52
|
-
"@integraledger/lcp-kernel": "0.
|
|
52
|
+
"@integraledger/lcp-kernel": "0.12.0"
|
|
53
53
|
},
|
|
54
54
|
"license": "Apache-2.0",
|
|
55
55
|
"engines": {
|
package/src/manifest.ts
CHANGED
|
@@ -9,6 +9,16 @@ import type { BindingManifest } from "@integraledger/lcp-binding-core";
|
|
|
9
9
|
* in `PaymentAuthorized`/`PaymentCharged` event data, so `recover`/`enumerate` decode the event and read
|
|
10
10
|
* `salt` (proven on-chain).
|
|
11
11
|
*
|
|
12
|
+
* **NO `offCanonical`, and that silence is examined rather than default.** §8.3.1 admits the marker only
|
|
13
|
+
* where the field is "specified as client-chosen or as a deterministic derivation that excludes
|
|
14
|
+
* `atrHash`", and `salt` meets NEITHER disjunct: `charge`/`authorize` are
|
|
15
|
+
* `onlySender(paymentInfo.operator)`, so the SERVICE constructs the whole struct and the payer signs only
|
|
16
|
+
* a hash of it, and the welded value IS `uint256(atrHash)` — a derivation that includes it. Nothing
|
|
17
|
+
* on-chain constrains the field's value, which is §8.3.1's Tier A condition. The x402 sibling declares
|
|
18
|
+
* `offCanonical` over the EIP-3009 nonce because THAT field is payer-chosen and its weld therefore needs
|
|
19
|
+
* a cooperating client stack; this one needs none, so mirroring the sibling would publish a
|
|
20
|
+
* controlled-client caveat that does not apply here.
|
|
21
|
+
*
|
|
12
22
|
* **recovery.forwardIndexable = false** — the criterion is enumeration bound to a GIVEN atrHash, and this
|
|
13
23
|
* rail cannot do it. `paymentInfoHash` is the only indexed topic, and it is a hash of the whole
|
|
14
24
|
* `PaymentInfo` struct: a caller holding the complete struct could topic-filter, but a caller holding only
|
|
@@ -63,7 +73,7 @@ export const ESCROW_MANIFEST: BindingManifest = {
|
|
|
63
73
|
indexing: "event-data-scan:paymentInfoHash",
|
|
64
74
|
finality: {
|
|
65
75
|
reversible: true,
|
|
66
|
-
note: 'on-rail void/refund within refundExpiry — not dispute resolution (RCS-5); capture is not reversed, refund is a fresh on-rail remedy The atrHash welded into PaymentInfo.salt MUST be per-transaction: salt is specified as the struct\'s entropy source ("a source of entropy to ensure unique hashes across different payments") and an atrHash carries none, so a repeat purchase under one ATR with the same payer, caps and expiries produces a PaymentInfo the escrow has already seen and reverts.',
|
|
76
|
+
note: 'on-rail void/refund within refundExpiry — not dispute resolution (RCS-5); capture is not reversed, refund is a fresh on-rail remedy. The atrHash welded into PaymentInfo.salt MUST be per-transaction: salt is specified as the struct\'s entropy source ("a source of entropy to ensure unique hashes across different payments") and an atrHash carries none, so a repeat purchase under one ATR with the same payer, caps and expiries produces a PaymentInfo the escrow has already seen and reverts.',
|
|
67
77
|
},
|
|
68
78
|
weldGrades: {
|
|
69
79
|
ERC3009: "signature",
|