You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: reconcile the two identity registries, and cap the faucet globally
Three defects found on a pass over the parts where one component mirrors another.
**subTier disagreed across the two IIdentityRegistry implementations.**
`CleanverseIdentityRegistry._toUint8` folds anything above 99 to 0 and blocks
pricing; the devnet mirror returned it verbatim and the relayer clamped to 255.
So an out-of-spec subTier of 150 was refused on Monad and priced at the top band
in tests — a devnet suite able to prove something production does not do. Both
now fold, pinned by testFuzz_outOfRangeSubTierReadsAsZero. 75 tests.
**Jurisdiction had three representations of one country.** The contract derives
uint16(bytes2(group)) — 21843 for "US" — while the relayer's lookup table
answered 1. The table is gone; the mirror now derives what the contract derives,
so any two-letter group works and there is no map left to drift. Dormant today,
since Cleanverse leaves the group empty and Covenant reads 0 as unconstrained.
**The faucet had no global limit.** Its per-address cooldown was not a cap:
cycling fresh addresses walks past it, and each pass mints 250,000 cvaUSD. No
contract is at risk — every cash leg pulls only from msg.sender — but the
settlement token could be inflated without bound. A rolling-hour cap now sits
alongside the cooldown, counted only after a mint actually lands so a Cleanverse
timeout does not consume the budget.
Also corrects STATIC-ANALYSIS.md. It claimed the reentrancy ordering was safe
only because cvaUSD has no transfer callback. It is safe regardless: a reentrant
settle burns the note, so fund's trailing _transfer then reverts on a token that
no longer exists and unwinds everything, and the mirror case in settle behaves
the same way including when obligor == supplier. The trailing writes are the
guard, not a residual risk.
@@ -158,10 +158,10 @@ Full adversarial review, including six owner-only findings that were deliberatel
158
158
forge build && forge test -vv
159
159
```
160
160
161
-
74 tests across three kinds:
161
+
75 tests across three kinds:
162
162
163
163
-**49 example tests** — happy-path lifecycle, subTier repricing, `AlreadyConfirmed` / `AlreadyOriginated` replay guards, escrow and token-level compliance gates, EIP-712 signed confirmation (valid / expired / wrong-signer), and the live identity adapter against a mock CVI that reproduces the real contract's revert-on-missing-pass behaviour.
164
-
-**16 property tests** ([`test/Fuzz.t.sol`](test/Fuzz.t.sol)) — 256 randomised runs each. The rate curve is monotonic across all 256 subTiers and never prices above face; two different obligations never share a key; a whole lifecycle conserves value for any face value and any maturity; no unverified address anywhere in the address space can hold a note.
164
+
-**17 property tests** ([`test/Fuzz.t.sol`](test/Fuzz.t.sol)) — 256 randomised runs each. The rate curve is monotonic across all 256 subTiers and never prices above face; two different obligations never share a key; a whole lifecycle conserves value for any face value and any maturity; no unverified address anywhere in the address space can hold a note.
165
165
-**9 system invariants** ([`test/Invariant.t.sol`](test/Invariant.t.sol)) — 12,800 randomly ordered calls each, from nine wallets spread across every band, with identity revocation and time skips mixed in. The escrow's balance is zero at every step, value is conserved, and no note is ever priced off the published curve.
166
166
167
167
**Coverage: 97.5% of lines and 94.1% of branches across the five deployed contracts** (`forge coverage`). `ObligationRegistry` — the core mechanic — is at 100% on every metric.
@@ -59,6 +74,13 @@ export async function POST(request: Request) {
59
74
returnbad(`This wallet was onboarded recently. Try again in ${minutes} minute${minutes===1 ? "" : "s"}.`,429);
60
75
}
61
76
77
+
if(!globalBudgetLeft()){
78
+
returnbad(
79
+
`The desk has onboarded ${GLOBAL_MAX_PER_WINDOW} wallets in the past hour, which is its cap. Try again shortly, or switch to Demo mode — it needs no wallet at all.`,
0 commit comments