@integraledger/lcp-binding-evm-common 0.15.0 → 0.16.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 CHANGED
@@ -1,5 +1,86 @@
1
1
  # @integraledger/lcp-binding-evm-common
2
2
 
3
+ ## 0.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2fd5eb2: `isEasValidAsOf` now bounds the validity interval from below.
8
+
9
+ The predicate answers "was this attestation valid AS OF the settlement". It gated on existence, revocation
10
+ and expiry — three facts that all bound the interval from ABOVE — and never read `att.time`, EAS's creation
11
+ timestamp, which was decoded and used by nothing. So an attestation minted the day after a settlement was
12
+ reported valid as of that settlement: backdating by omission, and the one direction an attester can exploit
13
+ after the fact.
14
+
15
+ `att.time > asOfUnixSeconds` is now a refusal. The boundary is inclusive in the same sense as the other
16
+ two: revoked or expired AT the as-of second is invalid, attested AT the as-of second is valid.
17
+
18
+ **This changes published behaviour** — a caller passing an attestation newer than its as-of instant now
19
+ gets `false` where it previously got `true`.
20
+ - bb48020: An RPC outage is no longer reported as a forged signature.
21
+
22
+ `verifyAcceptanceSignature` wrapped the bound verification call in `try { … } catch { return false }`. For
23
+ `eip191` and `eip712` that closure is pure offline recovery, so a throw genuinely is the signature. For
24
+ `evm:erc1271` and `evm:erc6492` it is an on-chain call, and viem has already drawn the line inside it: a
25
+ signature the validator rejects makes the deployless call revert, viem catches its own `VerificationError`
26
+ and returns `false`, and it rethrows only for an HTTP 429, a timeout, or a node that answered garbage.
27
+ Swallowing that rethrow turned every rate-limited RPC into `acceptance/bad-signature` — "signature did not
28
+ verify" — publishing a valid buyer acceptance as a forgery, a verdict the next run reverses. The package's
29
+ own README argued the opposite doctrine three paragraphs above the code: "'not verified' and 'verified as
30
+ forged' are different facts."
31
+
32
+ The guard is now scheme-shaped: it wraps the offline recovery only, and an on-chain call's throw
33
+ propagates. **This changes published behaviour** — a caller of `verifyAcceptanceSignature` or of the
34
+ `SignatureVerifier` returned by `makeEvmAcceptanceVerifier`, on a smart-account scheme, must now handle a
35
+ rejection where it previously received `false`. That is the point of the change: the two facts were
36
+ indistinguishable and one of them was wrong. The `false` verdict for a signature the chain actually
37
+ rejected is unchanged.
38
+
39
+ `authority` gains no behaviour change, only the contract in writing: `SignatureVerifier.verify` returning
40
+ `false` means CHECKED AND INVALID, and `verifyAcceptance` deliberately does not catch a rejection from the
41
+ port, because `acceptance/bad-signature` is a claim about the record and an unreachable node has made none.
42
+
43
+ The replaced test asserted the defect outright — "an on-chain call that throws is reported as false, not
44
+ propagated (a verifier must not crash)".
45
+
46
+ ### Patch Changes
47
+
48
+ - Updated dependencies [bb48020]
49
+ - @integraledger/lcp-authority@0.16.0
50
+ - @integraledger/lcp-binding-core@0.16.0
51
+ - @integraledger/lcp-kernel@0.16.0
52
+
53
+ ## 0.15.1
54
+
55
+ ### Patch Changes
56
+
57
+ - 1da6c07: `verifyEip3009Signature` threw on an attacker-chosen signature instead of answering `false`. Its docblock
58
+ promises the opposite — it "Answers `false` rather than throwing for the two UNTRUSTED inputs" — and a
59
+ seller pre-flighting an inbound x402 `PaymentPayload` was crashed by a hand-built 65-byte string that costs
60
+ no key, no chain and no funds to produce.
61
+
62
+ `isCanonicalSignature` checked shape, low-s and `v ∈ {27, 28}`, and not that `r` and `s` are in range.
63
+ Measured: `r=0x11…, s=0` threw, `r=0, s=0x11…` threw, and `r` at or above `n` threw. `"not-a-signature"`
64
+ was the only malformed shape the suite drove, and the regex already refused that — so every gate past the
65
+ regex was untested against a hostile value.
66
+
67
+ The range rules are the token's, on the same footing low-s is: `ecrecover` answers the ZERO ADDRESS rather
68
+ than reverting for `r = 0`, `s = 0` or a component at or above `n`, and OpenZeppelin's `ECRecover.recover`
69
+ — which `FiatTokenV2` routes through — reverts on the zero signer. `1 <= r < n` and `s !== 0` are now part
70
+ of the predicate. Low-s already caps `s` below `n/2`, so on the `s` side the only value the rule adds is
71
+ zero.
72
+
73
+ The gates went into the predicate rather than a `try` in the caller deliberately: a `try` would also swallow
74
+ an unencodable `typedData`, which is this deployment's own domain being wrong, and would tell an operator
75
+ with a mis-copied `tokenName` that every honest payer is a forger. That case still throws, as the head note
76
+ reserves.
77
+ - Updated dependencies [83ae16e]
78
+ - Updated dependencies [431b8ec]
79
+ - Updated dependencies [9c42f73]
80
+ - @integraledger/lcp-kernel@0.15.1
81
+ - @integraledger/lcp-binding-core@0.15.1
82
+ - @integraledger/lcp-authority@0.15.1
83
+
3
84
  ## 0.15.0
4
85
 
5
86
  ### Patch Changes
@@ -69,8 +150,8 @@
69
150
  `buildEip3009TypedData` has been here since the canonical EVM binding shipped, and nothing beside it could
70
151
  answer whether a given authorization was actually signed by the account it names. So a seller surface
71
152
  holding a payer's credential had three bad options: take the signature on trust until settlement, grow a
72
- viem dependency of its own, or reimplement recovery. `seller-mpp` needs exactly this for the 2026-08-24
73
- audit's C-28, and `seller-x402` already reaches here for `makeEvmAcceptanceVerifier` — the commons owns EVM
153
+ viem dependency of its own, or reimplement recovery. A seller MPP surface needs exactly this to check a payer's
154
+ authorization before settlement, and `seller-x402` already reaches here for `makeEvmAcceptanceVerifier` — the commons owns EVM
74
155
  crypto, and this is the piece that was missing.
75
156
 
76
157
  ⭐ **`ecrecover`, because that is what the TOKEN does.** `FiatTokenV2.transferWithAuthorization` recovers the
@@ -157,7 +238,7 @@ and a large number of documentation claims were corrected against the host speci
157
238
  First public release.
158
239
 
159
240
  `0.9.0` is deliberate: this is a release candidate for 1.0, not a preview. The implementation is complete
160
- against LCP v1.38 and certified by the conformance corpus, and the remaining distance to 1.0 is the
241
+ against the published LCP specification and certified by the conformance corpus, and the remaining distance to 1.0 is the
161
242
  specification's own — the standard is still moving through its steering committee, and this package will not
162
243
  claim a stability its protocol has not yet promised.
163
244
 
package/README.md CHANGED
@@ -39,13 +39,20 @@ const ok = await verifyAcceptanceSignature(acceptance);
39
39
  const onChain = await verifyAcceptanceSignature(acceptance, smartAccountOpts);
40
40
  ```
41
41
 
42
- ## Three outcomes, kept distinct
42
+ ## Four outcomes, kept distinct
43
43
 
44
44
  A malformed **configuration** throws — an absent chain id or a missing client is an integration error the
45
45
  caller must fix. A malformed **record** throws — an unparseable timestamp is a different fact from a
46
46
  forged signature, and letting it hide behind a bad-signature verdict would lose that. A malformed
47
47
  **signature** returns `false` — recovery over forged bytes fails deep in curve math, and a verifier that
48
- crashes on a forgery cannot report the forgery.
48
+ crashes on a forgery cannot report the forgery. A chain that could not **answer** throws — an HTTP 429 or
49
+ a timeout says nothing whatever about the signature, and reporting it as `false` publishes a valid buyer
50
+ acceptance as a forgery, a verdict the next run reverses.
51
+
52
+ The last two are told apart by the scheme, not by the error class: `eip191` and `eip712` verify by pure
53
+ offline recovery, so a throw there is the signature; `erc1271` and `erc6492` verify on-chain, and viem
54
+ already returns `false` for a signature its validator rejects and throws only when the node could not be
55
+ reached.
49
56
 
50
57
  Collapsing any two of those into one another is how a verifier ends up reporting the wrong thing about
51
58
  the one case it exists for.
package/dist/eas.d.ts CHANGED
@@ -84,10 +84,19 @@ export interface EasAttestation {
84
84
  /** Normalize viem's decoded tuple into an `EasAttestation` (idempotent field mapping; addresses lowercased). */
85
85
  export declare function decodeEasAttestation(raw: RawEasAttestation): EasAttestation;
86
86
  /**
87
- * Was this attestation valid AS OF `asOfUnixSeconds`? — it must exist, not have been revoked at/before the
88
- * as-of time (revocationTime 0 = never revoked), and not have expired by then (expirationTime 0 = no expiry).
89
- * EAS has no historical query, so a live read yields today's revocationTime; a settlement-time snapshot of the
90
- * uid is the authority (same as the Bitstring status-list as-of rule) this predicate states the semantics.
87
+ * Was this attestation valid AS OF `asOfUnixSeconds`? — it must exist, must already have been ATTESTED by
88
+ * then, must not have been revoked at/before the as-of time (revocationTime 0 = never revoked), and must
89
+ * not have expired by then (expirationTime 0 = no expiry). EAS has no historical query, so a live read
90
+ * yields today's revocationTime; a settlement-time snapshot of the uid is the authority (same as the
91
+ * Bitstring status-list as-of rule) — this predicate states the semantics.
92
+ *
93
+ * ⛔ **The `time` gate bounds the interval from BELOW, and it was missing until 2026-09-03.** Revocation
94
+ * and expiry both bound it from above, so an attestation minted the day AFTER a settlement was reported
95
+ * "valid as of" that settlement — backdating by omission, and the one direction an attester can exploit
96
+ * after the fact. `time` was decoded and read by nothing.
97
+ *
98
+ * All three boundaries are inclusive in the same sense: revoked or expired AT the as-of second is invalid,
99
+ * attested AT the as-of second is valid. The attestation was live for exactly that instant.
91
100
  */
92
101
  export declare function isEasValidAsOf(att: EasAttestation, asOfUnixSeconds: bigint): boolean;
93
102
  /** Read + normalize an EAS attestation by uid, over the injected viem client (imperative shell). */
package/dist/eas.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"eas.d.ts","sourceRoot":"","sources":["../src/eas.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAE9C,2GAA2G;AAC3G,eAAO,MAAM,uBAAuB;aAEhC,IAAI,EAAE,UAAU;aAChB,IAAI,EAAE,gBAAgB;aACtB,eAAe,EAAE,MAAM;aACvB,MAAM;iBAAK,IAAI,EAAE,KAAK;iBAAE,IAAI,EAAE,SAAS;;aACvC,OAAO;iBAEH,IAAI,EAAE,OAAO;iBACb,IAAI,EAAE,EAAE;iBACR,UAAU;qBACN,IAAI,EAAE,KAAK;qBAAE,IAAI,EAAE,SAAS;;qBAC5B,IAAI,EAAE,QAAQ;qBAAE,IAAI,EAAE,SAAS;;qBAC/B,IAAI,EAAE,MAAM;qBAAE,IAAI,EAAE,QAAQ;;qBAC5B,IAAI,EAAE,gBAAgB;qBAAE,IAAI,EAAE,QAAQ;;qBACtC,IAAI,EAAE,gBAAgB;qBAAE,IAAI,EAAE,QAAQ;;qBACtC,IAAI,EAAE,QAAQ;qBAAE,IAAI,EAAE,SAAS;;qBAC/B,IAAI,EAAE,WAAW;qBAAE,IAAI,EAAE,SAAS;;qBAClC,IAAI,EAAE,UAAU;qBAAE,IAAI,EAAE,SAAS;;qBACjC,IAAI,EAAE,WAAW;qBAAE,IAAI,EAAE,MAAM;;qBAC/B,IAAI,EAAE,MAAM;qBAAE,IAAI,EAAE,OAAO;;;EAK7B,CAAC;AAEX,8FAA8F;AAC9F,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,GAAG,CAAC;IACZ,SAAS,EAAE,GAAG,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,GAAG,CAAC;CACX;AAED,wGAAwG;AACxG,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,GAAG,CAAC;IACZ,SAAS,EAAE,GAAG,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,GAAG,CAAC;IACV,sGAAsG;IACtG,MAAM,EAAE,OAAO,CAAC;CACjB;AAKD,gHAAgH;AAChH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,iBAAiB,GAAG,cAAc,CAc3E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,cAAc,EACnB,eAAe,EAAE,MAAM,GACtB,OAAO,CAOT;AAED,oGAAoG;AACpG,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnC,OAAO,CAAC,cAAc,CAAC,CAQzB"}
1
+ {"version":3,"file":"eas.d.ts","sourceRoot":"","sources":["../src/eas.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAE9C,2GAA2G;AAC3G,eAAO,MAAM,uBAAuB;aAEhC,IAAI,EAAE,UAAU;aAChB,IAAI,EAAE,gBAAgB;aACtB,eAAe,EAAE,MAAM;aACvB,MAAM;iBAAK,IAAI,EAAE,KAAK;iBAAE,IAAI,EAAE,SAAS;;aACvC,OAAO;iBAEH,IAAI,EAAE,OAAO;iBACb,IAAI,EAAE,EAAE;iBACR,UAAU;qBACN,IAAI,EAAE,KAAK;qBAAE,IAAI,EAAE,SAAS;;qBAC5B,IAAI,EAAE,QAAQ;qBAAE,IAAI,EAAE,SAAS;;qBAC/B,IAAI,EAAE,MAAM;qBAAE,IAAI,EAAE,QAAQ;;qBAC5B,IAAI,EAAE,gBAAgB;qBAAE,IAAI,EAAE,QAAQ;;qBACtC,IAAI,EAAE,gBAAgB;qBAAE,IAAI,EAAE,QAAQ;;qBACtC,IAAI,EAAE,QAAQ;qBAAE,IAAI,EAAE,SAAS;;qBAC/B,IAAI,EAAE,WAAW;qBAAE,IAAI,EAAE,SAAS;;qBAClC,IAAI,EAAE,UAAU;qBAAE,IAAI,EAAE,SAAS;;qBACjC,IAAI,EAAE,WAAW;qBAAE,IAAI,EAAE,MAAM;;qBAC/B,IAAI,EAAE,MAAM;qBAAE,IAAI,EAAE,OAAO;;;EAK7B,CAAC;AAEX,8FAA8F;AAC9F,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,GAAG,CAAC;IACZ,SAAS,EAAE,GAAG,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,GAAG,CAAC;CACX;AAED,wGAAwG;AACxG,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,GAAG,CAAC;IACZ,SAAS,EAAE,GAAG,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,GAAG,CAAC;IACV,sGAAsG;IACtG,MAAM,EAAE,OAAO,CAAC;CACjB;AAKD,gHAAgH;AAChH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,iBAAiB,GAAG,cAAc,CAc3E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,cAAc,EACnB,eAAe,EAAE,MAAM,GACtB,OAAO,CAQT;AAED,oGAAoG;AACpG,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnC,OAAO,CAAC,cAAc,CAAC,CAQzB"}
package/dist/eas.js CHANGED
@@ -43,14 +43,25 @@ export function decodeEasAttestation(raw) {
43
43
  };
44
44
  }
45
45
  /**
46
- * Was this attestation valid AS OF `asOfUnixSeconds`? — it must exist, not have been revoked at/before the
47
- * as-of time (revocationTime 0 = never revoked), and not have expired by then (expirationTime 0 = no expiry).
48
- * EAS has no historical query, so a live read yields today's revocationTime; a settlement-time snapshot of the
49
- * uid is the authority (same as the Bitstring status-list as-of rule) this predicate states the semantics.
46
+ * Was this attestation valid AS OF `asOfUnixSeconds`? — it must exist, must already have been ATTESTED by
47
+ * then, must not have been revoked at/before the as-of time (revocationTime 0 = never revoked), and must
48
+ * not have expired by then (expirationTime 0 = no expiry). EAS has no historical query, so a live read
49
+ * yields today's revocationTime; a settlement-time snapshot of the uid is the authority (same as the
50
+ * Bitstring status-list as-of rule) — this predicate states the semantics.
51
+ *
52
+ * ⛔ **The `time` gate bounds the interval from BELOW, and it was missing until 2026-09-03.** Revocation
53
+ * and expiry both bound it from above, so an attestation minted the day AFTER a settlement was reported
54
+ * "valid as of" that settlement — backdating by omission, and the one direction an attester can exploit
55
+ * after the fact. `time` was decoded and read by nothing.
56
+ *
57
+ * All three boundaries are inclusive in the same sense: revoked or expired AT the as-of second is invalid,
58
+ * attested AT the as-of second is valid. The attestation was live for exactly that instant.
50
59
  */
51
60
  export function isEasValidAsOf(att, asOfUnixSeconds) {
52
61
  if (!att.exists)
53
62
  return false;
63
+ if (att.time > asOfUnixSeconds)
64
+ return false;
54
65
  if (att.revocationTime !== 0n && att.revocationTime <= asOfUnixSeconds)
55
66
  return false;
56
67
  if (att.expirationTime !== 0n && att.expirationTime <= asOfUnixSeconds)
package/dist/eas.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"eas.js","sourceRoot":"","sources":["../src/eas.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"eas.js","sourceRoot":"","sources":["../src/eas.ts"],"names":[],"mappings":"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"}
package/dist/eip3009.d.ts CHANGED
@@ -104,10 +104,11 @@ export interface LcpEvmSigner {
104
104
  /**
105
105
  * Whether the 65-byte signature is one `FiatTokenV2` will accept at all, before asking who signed it.
106
106
  *
107
- * Not a style check. Both rules come straight out of the token's `ECRecover.sol`, and skipping either one
108
- * turns {@link verifyEip3009Signature} from *"the chain will accept this"* into *"some chain might"*.
107
+ * Not a style check. Every rule comes straight out of the token's `ECRecover.sol` and the `ecrecover`
108
+ * precompile beneath it, and skipping any one turns {@link verifyEip3009Signature} from *"the chain will
109
+ * accept this"* into *"some chain might"* — or, for the range rules, into a throw.
109
110
  *
110
- * ⭐ Exported so the boundary is reachable by a test. `s === n/2` is ACCEPTED (the token's guard is
111
+ * ⭐ Exported so the boundaries are reachable by a test. `s === n/2` is ACCEPTED (the token's guard is
111
112
  * `s > n/2`), and no signing run will ever produce that value — so an off-by-one there is invisible from
112
113
  * the public predicate, where such a signature simply fails to recover and answers `false` either way.
113
114
  */
@@ -1 +1 @@
1
- {"version":3,"file":"eip3009.d.ts","sourceRoot":"","sources":["../src/eip3009.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"eip3009.d.ts","sourceRoot":"","sources":["../src/eip3009.ts"],"names":[],"mappings":"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"}
package/dist/eip3009.js CHANGED
@@ -80,13 +80,25 @@ export function buildEip3009TypedData(i) {
80
80
  * credential the chain refuses, and the seller has already served the resource by then.
81
81
  */
82
82
  const SECP256K1_HALF_N = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0n;
83
+ /**
84
+ * secp256k1's group order — the range `ecrecover` itself requires of `r` and `s`.
85
+ *
86
+ * ⛔ **The gate low-s does not cover, and the one a hostile counterparty reaches for.** `ecrecover` answers
87
+ * the ZERO ADDRESS rather than reverting for `r = 0`, `s = 0`, or either component at or above `n`, and
88
+ * OpenZeppelin's `ECRecover.recover` — which `FiatTokenV2` routes through — then reverts on the zero
89
+ * signer. These are refusals of the TOKEN on exactly the footing low-s is, so they belong in the same
90
+ * predicate rather than in a caller's `try`. Low-s already caps `s` below `n/2`, so on the `s` side the
91
+ * only value this rule adds is zero.
92
+ */
93
+ const SECP256K1_N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
83
94
  /**
84
95
  * Whether the 65-byte signature is one `FiatTokenV2` will accept at all, before asking who signed it.
85
96
  *
86
- * Not a style check. Both rules come straight out of the token's `ECRecover.sol`, and skipping either one
87
- * turns {@link verifyEip3009Signature} from *"the chain will accept this"* into *"some chain might"*.
97
+ * Not a style check. Every rule comes straight out of the token's `ECRecover.sol` and the `ecrecover`
98
+ * precompile beneath it, and skipping any one turns {@link verifyEip3009Signature} from *"the chain will
99
+ * accept this"* into *"some chain might"* — or, for the range rules, into a throw.
88
100
  *
89
- * ⭐ Exported so the boundary is reachable by a test. `s === n/2` is ACCEPTED (the token's guard is
101
+ * ⭐ Exported so the boundaries are reachable by a test. `s === n/2` is ACCEPTED (the token's guard is
90
102
  * `s > n/2`), and no signing run will ever produce that value — so an off-by-one there is invisible from
91
103
  * the public predicate, where such a signature simply fails to recover and answers `false` either way.
92
104
  */
@@ -98,7 +110,16 @@ export function isCanonicalSignature(signature) {
98
110
  const parts = /^0x([0-9a-fA-F]{64})([0-9a-fA-F]{64})([0-9a-fA-F]{2})$/.exec(signature);
99
111
  if (parts === null)
100
112
  return false;
101
- if (BigInt(`0x${parts[2]}`) > SECP256K1_HALF_N)
113
+ const r = BigInt(`0x${parts[1]}`);
114
+ const s = BigInt(`0x${parts[2]}`);
115
+ // RANGE before malleability. `1 <= r < n` and `s !== 0`: outside these the recovery does not merely
116
+ // yield the wrong answer, it raises — which is how an attacker-chosen 65 bytes, costing no key, no
117
+ // chain and no funds, crashed a seller pre-flighting an inbound payload instead of being refused.
118
+ if (r === 0n || r >= SECP256K1_N)
119
+ return false;
120
+ if (s === 0n)
121
+ return false;
122
+ if (s > SECP256K1_HALF_N)
102
123
  return false;
103
124
  const v = Number.parseInt(parts[3], 16);
104
125
  return v === 27 || v === 28;
@@ -1 +1 @@
1
- {"version":3,"file":"eip3009.js","sourceRoot":"","sources":["../src/eip3009.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"eip3009.js","sourceRoot":"","sources":["../src/eip3009.ts"],"names":[],"mappings":"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"}
package/dist/erc1271.d.ts CHANGED
@@ -106,7 +106,7 @@ export interface AcceptanceVerifyOpts {
106
106
  * Verify an acceptance signature. EOA schemes recover offline; smart-account schemes verify on-chain through
107
107
  * `opts.client`. Returns a boolean (the port contract).
108
108
  *
109
- * THREE OUTCOMES, and keeping them distinct is the whole point:
109
+ * FOUR OUTCOMES, and keeping them distinct is the whole point:
110
110
  *
111
111
  * - **A malformed CONFIGURATION throws** (absent `chainId` for the envelope, absent client for a smart
112
112
  * account) — an integration error the caller must fix, not a fact about the record.
@@ -118,9 +118,18 @@ export interface AcceptanceVerifyOpts {
118
118
  * curve math ("Point is not on curve"), and a verifier that crashes on a forgery cannot report the
119
119
  * forgery — the adversarial case is exactly the one verification exists for. An unrecoverable signature
120
120
  * is definitively not a valid signature, so the walk records an honest `failed(verification-failure)`.
121
+ * - **A chain that could not ANSWER throws.** ⛔ This one used to be swallowed as `false`, and it is the
122
+ * reason the guard is now scheme-shaped rather than wrapped around everything. For an EOA scheme the
123
+ * bound closure is pure recovery with no I/O, so a throw there IS the signature. For `evm:erc1271` and
124
+ * `evm:erc6492` it is an on-chain call, and viem has already drawn this line inside it: a signature the
125
+ * validator rejects makes the deployless call revert, viem catches its own `VerificationError` and
126
+ * returns `false`, and it rethrows only for an HTTP 429, a timeout, or a node that answered garbage.
127
+ * Collapsing that into `false` published a valid buyer acceptance as `acceptance/bad-signature` —
128
+ * "signature did not verify" — for as long as an RPC was rate-limited, a verdict that reverses on the
129
+ * next run. "Not verified" and "verified as forged" are different facts.
121
130
  *
122
- * The guard therefore wraps ONLY the recovery/verification calls. Widening it to cover the normalization
123
- * above would collapse the second outcome into the third.
131
+ * The guard therefore wraps ONLY the OFFLINE recovery calls. Widening it over the normalization above
132
+ * collapses the second outcome into the third; widening it over the on-chain call collapses the fourth.
124
133
  */
125
134
  export declare function verifyAcceptanceSignature(input: AcceptanceSignatureInput, opts?: AcceptanceVerifyOpts): Promise<boolean>;
126
135
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"erc1271.d.ts","sourceRoot":"","sources":["../src/erc1271.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"erc1271.d.ts","sourceRoot":"","sources":["../src/erc1271.ts"],"names":[],"mappings":"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"}
package/dist/erc1271.js CHANGED
@@ -63,7 +63,7 @@ export function isEvmPayloadType(v) {
63
63
  * Verify an acceptance signature. EOA schemes recover offline; smart-account schemes verify on-chain through
64
64
  * `opts.client`. Returns a boolean (the port contract).
65
65
  *
66
- * THREE OUTCOMES, and keeping them distinct is the whole point:
66
+ * FOUR OUTCOMES, and keeping them distinct is the whole point:
67
67
  *
68
68
  * - **A malformed CONFIGURATION throws** (absent `chainId` for the envelope, absent client for a smart
69
69
  * account) — an integration error the caller must fix, not a fact about the record.
@@ -75,15 +75,27 @@ export function isEvmPayloadType(v) {
75
75
  * curve math ("Point is not on curve"), and a verifier that crashes on a forgery cannot report the
76
76
  * forgery — the adversarial case is exactly the one verification exists for. An unrecoverable signature
77
77
  * is definitively not a valid signature, so the walk records an honest `failed(verification-failure)`.
78
+ * - **A chain that could not ANSWER throws.** ⛔ This one used to be swallowed as `false`, and it is the
79
+ * reason the guard is now scheme-shaped rather than wrapped around everything. For an EOA scheme the
80
+ * bound closure is pure recovery with no I/O, so a throw there IS the signature. For `evm:erc1271` and
81
+ * `evm:erc6492` it is an on-chain call, and viem has already drawn this line inside it: a signature the
82
+ * validator rejects makes the deployless call revert, viem catches its own `VerificationError` and
83
+ * returns `false`, and it rethrows only for an HTTP 429, a timeout, or a node that answered garbage.
84
+ * Collapsing that into `false` published a valid buyer acceptance as `acceptance/bad-signature` —
85
+ * "signature did not verify" — for as long as an RPC was rate-limited, a verdict that reverses on the
86
+ * next run. "Not verified" and "verified as forged" are different facts.
78
87
  *
79
- * The guard therefore wraps ONLY the recovery/verification calls. Widening it to cover the normalization
80
- * above would collapse the second outcome into the third.
88
+ * The guard therefore wraps ONLY the OFFLINE recovery calls. Widening it over the normalization above
89
+ * collapses the second outcome into the third; widening it over the on-chain call collapses the fourth.
81
90
  */
82
91
  export async function verifyAcceptanceSignature(input, opts) {
83
92
  // Normalization + configuration: OUTSIDE the guard, so both stay loud.
84
- const recover = prepareRecovery(input, opts);
93
+ const { run, offline } = prepareRecovery(input, opts);
94
+ // On-chain: viem's verdict is the whole answer, and anything it throws is the chain failing to give one.
95
+ if (!offline)
96
+ return await run();
85
97
  try {
86
- return await recover();
98
+ return await run();
87
99
  }
88
100
  catch {
89
101
  return false; // the signature itself did not recover — not a valid signature
@@ -96,6 +108,12 @@ class ConfigurationError extends Error {
96
108
  * Normalize the record's fields and bind the scheme-appropriate recovery call, returning it UNEXECUTED so
97
109
  * the caller can guard the recovery alone. Anything wrong with the configuration or the record's own fields
98
110
  * throws from here, before the guard exists.
111
+ *
112
+ * `offline` says whether the bound call does any I/O, and it is the caller's whole basis for deciding
113
+ * whether a throw is a fact about the signature or a fact about the network. It is derived from the scheme
114
+ * here — where the branch is already taken — rather than sniffed off an error class at the catch site: an
115
+ * allowlist of transport-error names is a subject set that goes stale silently, and getting it wrong in
116
+ * that direction reports a forgery.
99
117
  */
100
118
  function prepareRecovery(input, opts) {
101
119
  // A signer that is not an address is a malformed RECORD, not a forgery — thrown here, before the guard,
@@ -119,28 +137,40 @@ function prepareRecovery(input, opts) {
119
137
  // `opts` (not `opts?.`): the chainId gate above already proved it defined — an optional chain here
120
138
  // would be dead syntax guarding an impossible state, and the mutation run flags it as equivalent.
121
139
  const client = requireClient(opts.client, input.scheme);
122
- return () => client.verifyTypedData({ address: signer, ...typedData, signature });
140
+ return {
141
+ offline: false,
142
+ run: () => client.verifyTypedData({ address: signer, ...typedData, signature }),
143
+ };
123
144
  }
124
- return async () => {
125
- const recovered = await recoverTypedDataAddress({
126
- ...typedData,
127
- signature,
128
- });
129
- return recovered.toLowerCase() === signer.toLowerCase();
145
+ return {
146
+ offline: true,
147
+ run: async () => {
148
+ const recovered = await recoverTypedDataAddress({
149
+ ...typedData,
150
+ signature,
151
+ });
152
+ return recovered.toLowerCase() === signer.toLowerCase();
153
+ },
130
154
  };
131
155
  }
132
156
  // payloadType === "atrHash": the raw 32-byte atrHash signed as a personal_sign message.
133
157
  const raw = assertBytes32(input.atrHash); // throws loud on a malformed atrHash
134
158
  if (isContract) {
135
159
  const client = requireClient(opts?.client, input.scheme);
136
- return () => client.verifyMessage({ address: signer, message: { raw }, signature });
160
+ return {
161
+ offline: false,
162
+ run: () => client.verifyMessage({ address: signer, message: { raw }, signature }),
163
+ };
137
164
  }
138
- return async () => {
139
- const recovered = await recoverMessageAddress({
140
- message: { raw },
141
- signature,
142
- });
143
- return recovered.toLowerCase() === signer.toLowerCase();
165
+ return {
166
+ offline: true,
167
+ run: async () => {
168
+ const recovered = await recoverMessageAddress({
169
+ message: { raw },
170
+ signature,
171
+ });
172
+ return recovered.toLowerCase() === signer.toLowerCase();
173
+ },
144
174
  };
145
175
  }
146
176
  function requireClient(client, scheme) {
@@ -1 +1 @@
1
- {"version":3,"file":"erc1271.js","sourceRoot":"","sources":["../src/erc1271.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"erc1271.js","sourceRoot":"","sources":["../src/erc1271.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integraledger/lcp-binding-evm-common",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Shared EVM machinery for LCP rail bindings: typed-data construction and signature verification.",
5
5
  "keywords": [
6
6
  "lcp",
@@ -43,9 +43,9 @@
43
43
  },
44
44
  "homepage": "https://github.com/IntegraLedger/integra-protocol/tree/main/packages/binding-evm-common#readme",
45
45
  "dependencies": {
46
- "@integraledger/lcp-authority": "0.15.0",
47
- "@integraledger/lcp-binding-core": "0.15.0",
48
- "@integraledger/lcp-kernel": "0.15.0",
46
+ "@integraledger/lcp-authority": "0.16.0",
47
+ "@integraledger/lcp-binding-core": "0.16.0",
48
+ "@integraledger/lcp-kernel": "0.16.0",
49
49
  "viem": "2.55.19"
50
50
  },
51
51
  "devDependencies": {
package/src/eas.ts CHANGED
@@ -88,16 +88,26 @@ export function decodeEasAttestation(raw: RawEasAttestation): EasAttestation {
88
88
  }
89
89
 
90
90
  /**
91
- * Was this attestation valid AS OF `asOfUnixSeconds`? — it must exist, not have been revoked at/before the
92
- * as-of time (revocationTime 0 = never revoked), and not have expired by then (expirationTime 0 = no expiry).
93
- * EAS has no historical query, so a live read yields today's revocationTime; a settlement-time snapshot of the
94
- * uid is the authority (same as the Bitstring status-list as-of rule) this predicate states the semantics.
91
+ * Was this attestation valid AS OF `asOfUnixSeconds`? — it must exist, must already have been ATTESTED by
92
+ * then, must not have been revoked at/before the as-of time (revocationTime 0 = never revoked), and must
93
+ * not have expired by then (expirationTime 0 = no expiry). EAS has no historical query, so a live read
94
+ * yields today's revocationTime; a settlement-time snapshot of the uid is the authority (same as the
95
+ * Bitstring status-list as-of rule) — this predicate states the semantics.
96
+ *
97
+ * ⛔ **The `time` gate bounds the interval from BELOW, and it was missing until 2026-09-03.** Revocation
98
+ * and expiry both bound it from above, so an attestation minted the day AFTER a settlement was reported
99
+ * "valid as of" that settlement — backdating by omission, and the one direction an attester can exploit
100
+ * after the fact. `time` was decoded and read by nothing.
101
+ *
102
+ * All three boundaries are inclusive in the same sense: revoked or expired AT the as-of second is invalid,
103
+ * attested AT the as-of second is valid. The attestation was live for exactly that instant.
95
104
  */
96
105
  export function isEasValidAsOf(
97
106
  att: EasAttestation,
98
107
  asOfUnixSeconds: bigint,
99
108
  ): boolean {
100
109
  if (!att.exists) return false;
110
+ if (att.time > asOfUnixSeconds) return false;
101
111
  if (att.revocationTime !== 0n && att.revocationTime <= asOfUnixSeconds)
102
112
  return false;
103
113
  if (att.expirationTime !== 0n && att.expirationTime <= asOfUnixSeconds)
package/src/eip3009.ts CHANGED
@@ -159,13 +159,27 @@ export interface LcpEvmSigner {
159
159
  const SECP256K1_HALF_N =
160
160
  0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0n;
161
161
 
162
+ /**
163
+ * secp256k1's group order — the range `ecrecover` itself requires of `r` and `s`.
164
+ *
165
+ * ⛔ **The gate low-s does not cover, and the one a hostile counterparty reaches for.** `ecrecover` answers
166
+ * the ZERO ADDRESS rather than reverting for `r = 0`, `s = 0`, or either component at or above `n`, and
167
+ * OpenZeppelin's `ECRecover.recover` — which `FiatTokenV2` routes through — then reverts on the zero
168
+ * signer. These are refusals of the TOKEN on exactly the footing low-s is, so they belong in the same
169
+ * predicate rather than in a caller's `try`. Low-s already caps `s` below `n/2`, so on the `s` side the
170
+ * only value this rule adds is zero.
171
+ */
172
+ const SECP256K1_N =
173
+ 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
174
+
162
175
  /**
163
176
  * Whether the 65-byte signature is one `FiatTokenV2` will accept at all, before asking who signed it.
164
177
  *
165
- * Not a style check. Both rules come straight out of the token's `ECRecover.sol`, and skipping either one
166
- * turns {@link verifyEip3009Signature} from *"the chain will accept this"* into *"some chain might"*.
178
+ * Not a style check. Every rule comes straight out of the token's `ECRecover.sol` and the `ecrecover`
179
+ * precompile beneath it, and skipping any one turns {@link verifyEip3009Signature} from *"the chain will
180
+ * accept this"* into *"some chain might"* — or, for the range rules, into a throw.
167
181
  *
168
- * ⭐ Exported so the boundary is reachable by a test. `s === n/2` is ACCEPTED (the token's guard is
182
+ * ⭐ Exported so the boundaries are reachable by a test. `s === n/2` is ACCEPTED (the token's guard is
169
183
  * `s > n/2`), and no signing run will ever produce that value — so an off-by-one there is invisible from
170
184
  * the public predicate, where such a signature simply fails to recover and answers `false` either way.
171
185
  */
@@ -178,7 +192,14 @@ export function isCanonicalSignature(signature: string): boolean {
178
192
  signature,
179
193
  );
180
194
  if (parts === null) return false;
181
- if (BigInt(`0x${parts[2] as string}`) > SECP256K1_HALF_N) return false;
195
+ const r = BigInt(`0x${parts[1] as string}`);
196
+ const s = BigInt(`0x${parts[2] as string}`);
197
+ // RANGE before malleability. `1 <= r < n` and `s !== 0`: outside these the recovery does not merely
198
+ // yield the wrong answer, it raises — which is how an attacker-chosen 65 bytes, costing no key, no
199
+ // chain and no funds, crashed a seller pre-flighting an inbound payload instead of being refused.
200
+ if (r === 0n || r >= SECP256K1_N) return false;
201
+ if (s === 0n) return false;
202
+ if (s > SECP256K1_HALF_N) return false;
182
203
  const v = Number.parseInt(parts[3] as string, 16);
183
204
  return v === 27 || v === 28;
184
205
  }
package/src/erc1271.ts CHANGED
@@ -148,7 +148,7 @@ export interface AcceptanceVerifyOpts {
148
148
  * Verify an acceptance signature. EOA schemes recover offline; smart-account schemes verify on-chain through
149
149
  * `opts.client`. Returns a boolean (the port contract).
150
150
  *
151
- * THREE OUTCOMES, and keeping them distinct is the whole point:
151
+ * FOUR OUTCOMES, and keeping them distinct is the whole point:
152
152
  *
153
153
  * - **A malformed CONFIGURATION throws** (absent `chainId` for the envelope, absent client for a smart
154
154
  * account) — an integration error the caller must fix, not a fact about the record.
@@ -160,18 +160,29 @@ export interface AcceptanceVerifyOpts {
160
160
  * curve math ("Point is not on curve"), and a verifier that crashes on a forgery cannot report the
161
161
  * forgery — the adversarial case is exactly the one verification exists for. An unrecoverable signature
162
162
  * is definitively not a valid signature, so the walk records an honest `failed(verification-failure)`.
163
+ * - **A chain that could not ANSWER throws.** ⛔ This one used to be swallowed as `false`, and it is the
164
+ * reason the guard is now scheme-shaped rather than wrapped around everything. For an EOA scheme the
165
+ * bound closure is pure recovery with no I/O, so a throw there IS the signature. For `evm:erc1271` and
166
+ * `evm:erc6492` it is an on-chain call, and viem has already drawn this line inside it: a signature the
167
+ * validator rejects makes the deployless call revert, viem catches its own `VerificationError` and
168
+ * returns `false`, and it rethrows only for an HTTP 429, a timeout, or a node that answered garbage.
169
+ * Collapsing that into `false` published a valid buyer acceptance as `acceptance/bad-signature` —
170
+ * "signature did not verify" — for as long as an RPC was rate-limited, a verdict that reverses on the
171
+ * next run. "Not verified" and "verified as forged" are different facts.
163
172
  *
164
- * The guard therefore wraps ONLY the recovery/verification calls. Widening it to cover the normalization
165
- * above would collapse the second outcome into the third.
173
+ * The guard therefore wraps ONLY the OFFLINE recovery calls. Widening it over the normalization above
174
+ * collapses the second outcome into the third; widening it over the on-chain call collapses the fourth.
166
175
  */
167
176
  export async function verifyAcceptanceSignature(
168
177
  input: AcceptanceSignatureInput,
169
178
  opts?: AcceptanceVerifyOpts,
170
179
  ): Promise<boolean> {
171
180
  // Normalization + configuration: OUTSIDE the guard, so both stay loud.
172
- const recover = prepareRecovery(input, opts);
181
+ const { run, offline } = prepareRecovery(input, opts);
182
+ // On-chain: viem's verdict is the whole answer, and anything it throws is the chain failing to give one.
183
+ if (!offline) return await run();
173
184
  try {
174
- return await recover();
185
+ return await run();
175
186
  } catch {
176
187
  return false; // the signature itself did not recover — not a valid signature
177
188
  }
@@ -184,11 +195,17 @@ class ConfigurationError extends Error {}
184
195
  * Normalize the record's fields and bind the scheme-appropriate recovery call, returning it UNEXECUTED so
185
196
  * the caller can guard the recovery alone. Anything wrong with the configuration or the record's own fields
186
197
  * throws from here, before the guard exists.
198
+ *
199
+ * `offline` says whether the bound call does any I/O, and it is the caller's whole basis for deciding
200
+ * whether a throw is a fact about the signature or a fact about the network. It is derived from the scheme
201
+ * here — where the branch is already taken — rather than sniffed off an error class at the catch site: an
202
+ * allowlist of transport-error names is a subject set that goes stale silently, and getting it wrong in
203
+ * that direction reports a forgery.
187
204
  */
188
205
  function prepareRecovery(
189
206
  input: AcceptanceSignatureInput,
190
207
  opts?: AcceptanceVerifyOpts,
191
- ): () => Promise<boolean> {
208
+ ): { run: () => Promise<boolean>; offline: boolean } {
192
209
  // A signer that is not an address is a malformed RECORD, not a forgery — thrown here, before the guard,
193
210
  // for the same reason rfc3339ToUnixSeconds throws: recovery over it would fail deep in viem and read as
194
211
  // a bad-signature verdict about a signature nobody could have examined.
@@ -216,15 +233,21 @@ function prepareRecovery(
216
233
  // `opts` (not `opts?.`): the chainId gate above already proved it defined — an optional chain here
217
234
  // would be dead syntax guarding an impossible state, and the mutation run flags it as equivalent.
218
235
  const client = requireClient(opts.client, input.scheme);
219
- return () =>
220
- client.verifyTypedData({ address: signer, ...typedData, signature });
236
+ return {
237
+ offline: false,
238
+ run: () =>
239
+ client.verifyTypedData({ address: signer, ...typedData, signature }),
240
+ };
221
241
  }
222
- return async () => {
223
- const recovered = await recoverTypedDataAddress({
224
- ...typedData,
225
- signature,
226
- });
227
- return recovered.toLowerCase() === signer.toLowerCase();
242
+ return {
243
+ offline: true,
244
+ run: async () => {
245
+ const recovered = await recoverTypedDataAddress({
246
+ ...typedData,
247
+ signature,
248
+ });
249
+ return recovered.toLowerCase() === signer.toLowerCase();
250
+ },
228
251
  };
229
252
  }
230
253
 
@@ -232,15 +255,21 @@ function prepareRecovery(
232
255
  const raw = assertBytes32(input.atrHash); // throws loud on a malformed atrHash
233
256
  if (isContract) {
234
257
  const client = requireClient(opts?.client, input.scheme);
235
- return () =>
236
- client.verifyMessage({ address: signer, message: { raw }, signature });
258
+ return {
259
+ offline: false,
260
+ run: () =>
261
+ client.verifyMessage({ address: signer, message: { raw }, signature }),
262
+ };
237
263
  }
238
- return async () => {
239
- const recovered = await recoverMessageAddress({
240
- message: { raw },
241
- signature,
242
- });
243
- return recovered.toLowerCase() === signer.toLowerCase();
264
+ return {
265
+ offline: true,
266
+ run: async () => {
267
+ const recovered = await recoverMessageAddress({
268
+ message: { raw },
269
+ signature,
270
+ });
271
+ return recovered.toLowerCase() === signer.toLowerCase();
272
+ },
244
273
  };
245
274
  }
246
275