Commit 4ac5f8b
Payments rearchitecture: RFQ hardening + multi-asset bill weighting + facet split for EIP-170 (#133)
* fix(quotes): RFQ replay / freshness / cumulative TTL hardening
Three security findings on the RFQ quote paths, all closed:
- Job RFQ `requester` is signed but was not verified. Thread `msg.sender`
through `verifyAndMarkJobQuoteUsed`; reject when the verified address
does not match the quote's bound requester. Wildcard requesters
(`address(0)`) are also rejected.
- Service-creation quotes lacked the freshness check the per-job path
enforces. Add the same `block.timestamp <= details.timestamp + maxQuoteAge`
gate to `verifyQuoteBatch` so stale quotes cannot be redeemed long after
the protocol's max-quote-age window.
- `extendServiceFromQuotes` allowed unbounded cumulative TTL. Cap the
cumulative service TTL at `MAX_SERVICE_TTL` so repeated extensions
cannot push a single service past the protocol's lifetime ceiling.
New errors: `JobQuoteRequesterMismatch`, `QuoteTimestampStale`,
`CumulativeTtlExceeded`.
Tests added in `QuoteVerification.t.sol`, `QuoteExtension.t.sol`,
`RFQPaymentDistribution.t.sol` covering each guard. Full RFQ suite
57/57 passes.
* feat(payments): multi-asset subscription bill weighting
Restore the per-asset commitment system to subscription bills. Previously
the bill weighted by a single bond-asset cum-stake-seconds × the operator's
overall `ServiceOperator.exposureBps`, collapsing the protocol's
multi-asset security model. Now the bill iterates the operator's
`AssetSecurityCommitment[]` for the service and sums
`cumDelta_op_asset × commitmentBps_asset` (USD-normalized via oracle when
configured) across every (op, asset) pair the service requires.
- TWAP cursors are now keyed `(serviceId, operator, assetHash)` so per-asset
joins, leaves, and rejoins compute correct deltas.
- Activation pins a multi-asset baseline of
`Σ_op Σ_asset (delegation × commitmentBps × price)`.
- Bill amount + operator payout weight share the same per-asset stake-time
× exposure aggregate, so customer-fairness and operator-fairness move
together across assets.
- Fallback: when an operator has no per-asset commitments stored (services
that don't opt into the multi-asset commitment system), bill against the
bond asset at `ServiceOperator.exposureBps`. Mirrors the legacy
single-asset semantics so existing tests / services keep working.
Storage: replaces `_twapCursorByOp` (2-level) with `_twapCursorByOpAsset`
(3-level). Greenfield rename — no migration.
Full regression: 1450 / 1450 ✅.
* refactor(facets): split Payments + Services facets to fit EIP-170
Three facets exceeded the 24,576-byte runtime ceiling (TanglePaymentsFacet 31,846,
TangleServicesFacet 26,805, TanglePaymentsDistributionFacet 28,971), blocking
deployment on any chain that enforces EIP-170. Split the abstract `Payments`
contract into focused mixins so each facet inherits only its slice, and pull
validation pure-compute out of `ServicesApprovals` into an external library.
Payments split:
- `PaymentsCore` — shared events, `_activeServiceOperators`, `_depositToEscrow`
- `PaymentsEscrow` — `fundService`, `withdrawRemainingEscrow`
- `PaymentsBilling` — subscription bill flow, TWAP weighting, `_accrueOperatorWeights`
- `PaymentsDistribution` — distribution core, exposure fallback, baseline init
- `PaymentsRewards` — already-split rewards/admin (+ `getBillableServices`)
Subscription billing's distribution path is reached via a diamond self-call
(`ITanglePaymentsInternal.distributeBillWithKeeper`) so the distribution
machinery only contributes bytecode to the facet that hosts it. Same for
`initSubscriptionBaseline` (now on `TanglePaymentsDistributionFacet`).
`hasSecurityCommitments` is now computed inline in `_accrueOperatorWeights`
instead of via a second `_calculateEffectiveExposures` pass, which lets
`PaymentsBilling` drop its `PaymentsEffectiveExposure` inheritance entirely.
Services split:
- `AttestationLib` — shared `teeNonce` + `blsPopMessage` pure compute
- `ServiceValidationLib` — external library hosting `_validateTeeCommitments`,
`_validateSecurityCommitments`, `_requireBlsProofOfPossession`
- `ServicesApprovalsViews` — views facet, no longer inherited by `ServicesApprovals`
Final runtime sizes (default profile, optimizer on, runs=1):
- TanglePaymentsFacet 24,160 (-416 under EIP-170)
- TanglePaymentsDistributionFacet 18,144
- TanglePaymentsRewardsFacet 15,266
- TangleServicesFacet 21,741
- ServiceValidationLib 3,802 (deployed separately, auto-linked)
No storage layout changes. No diamond-side ABI changes — same selectors, just
routed to different physical facet contracts. Full regression: 1,450/1,450
tests pass.
---------
Co-authored-by: Drew Stone <drewstone329@gmail.com>1 parent 8b2777b commit 4ac5f8b
35 files changed
Lines changed: 1856 additions & 1299 deletions
File tree
- script
- src
- config
- core
- facets/tangle
- interfaces
- libraries
- test
- blueprints
- fuzz
- tangle
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
| 30 | + | |
| 31 | + | |
29 | 32 | | |
30 | 33 | | |
31 | 34 | | |
| |||
177 | 180 | | |
178 | 181 | | |
179 | 182 | | |
| 183 | + | |
180 | 184 | | |
181 | 185 | | |
182 | 186 | | |
183 | 187 | | |
184 | 188 | | |
185 | 189 | | |
186 | 190 | | |
| 191 | + | |
| 192 | + | |
187 | 193 | | |
188 | 194 | | |
189 | 195 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
| 29 | + | |
| 30 | + | |
28 | 31 | | |
29 | 32 | | |
30 | 33 | | |
| |||
398 | 401 | | |
399 | 402 | | |
400 | 403 | | |
| 404 | + | |
401 | 405 | | |
402 | 406 | | |
403 | 407 | | |
404 | 408 | | |
405 | 409 | | |
406 | 410 | | |
407 | 411 | | |
| 412 | + | |
| 413 | + | |
408 | 414 | | |
409 | 415 | | |
410 | 416 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
| 27 | + | |
| 28 | + | |
26 | 29 | | |
27 | 30 | | |
28 | 31 | | |
| |||
127 | 130 | | |
128 | 131 | | |
129 | 132 | | |
| 133 | + | |
130 | 134 | | |
131 | 135 | | |
132 | 136 | | |
133 | 137 | | |
134 | 138 | | |
135 | 139 | | |
136 | 140 | | |
| 141 | + | |
| 142 | + | |
137 | 143 | | |
138 | 144 | | |
139 | 145 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
33 | 34 | | |
34 | 35 | | |
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
38 | 39 | | |
39 | 40 | | |
| 41 | + | |
| 42 | + | |
40 | 43 | | |
41 | 44 | | |
42 | 45 | | |
| |||
1326 | 1329 | | |
1327 | 1330 | | |
1328 | 1331 | | |
| 1332 | + | |
1329 | 1333 | | |
1330 | 1334 | | |
1331 | 1335 | | |
1332 | 1336 | | |
1333 | 1337 | | |
1334 | 1338 | | |
1335 | 1339 | | |
| 1340 | + | |
| 1341 | + | |
1336 | 1342 | | |
1337 | 1343 | | |
1338 | 1344 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
410 | 410 | | |
411 | 411 | | |
412 | 412 | | |
413 | | - | |
414 | | - | |
415 | | - | |
416 | | - | |
417 | | - | |
418 | | - | |
419 | | - | |
420 | | - | |
421 | | - | |
422 | | - | |
423 | | - | |
424 | | - | |
425 | | - | |
426 | | - | |
427 | | - | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
428 | 424 | | |
429 | 425 | | |
430 | 426 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
60 | 67 | | |
61 | 68 | | |
62 | 69 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
82 | 82 | | |
83 | 83 | | |
84 | 84 | | |
85 | | - | |
| 85 | + | |
| 86 | + | |
86 | 87 | | |
87 | 88 | | |
88 | 89 | | |
| |||
132 | 133 | | |
133 | 134 | | |
134 | 135 | | |
135 | | - | |
| 136 | + | |
| 137 | + | |
136 | 138 | | |
137 | 139 | | |
138 | 140 | | |
| |||
174 | 176 | | |
175 | 177 | | |
176 | 178 | | |
177 | | - | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
178 | 182 | | |
179 | 183 | | |
180 | 184 | | |
| |||
0 commit comments