Redeploy PawnShop (with its LendingPool), LockDiscount and MilestoneBurn with the fixes below, from audit job e4a761c2-59b8-4c34-84fc-54fade5f665a (commit 5086b57). Keep the token and FloorRelay as deployed. Every other rule, number and interface stays as is.
CONSTRUCTOR PRESETS: attester = the deployed FloorRelay; identity.md questionHash = 0x71ed43868c5c61fe21b72bbbdcc09913d4952a113a393c526e49f3289edf4be1; newLoansPaused = true; same owner.
PAWNSHOP
- F1: startAuction requires floorFresh(collection) (revert StaleFloor); buyAuction reverts in the same block the auction started.
- F2: restartAuction(id), anyone, allowed after writeOffAuction or once an auction has sat at the terminal price for 7 days; requires a fresh floor, resets auctionStarted and auctionFloor, re-runs the curve; proceeds still flow through receiveRecovery and borrower surplus.
- F3: buyAuction reverts when the vault does not hold the collateral (no zero-price sale); writeOffAuction stays the settlement path for missing collateral.
- F5: markOverdue(id), anyone, from loan.due onward, books principal minus min(principal, storedFloor/2) through markAuctionLoss (non-decreasing, keyed by id).
- F6: pawn(collection, tokenId, termId, minPrincipal, maxFee); revert if principal < minPrincipal or fee > maxFee. The site passes the displayed values with 1% tolerance.
- F7: no auction bounty when msg.sender == loan.borrower; cap it at min(AUCTION_BOUNTY, principal / 100).
- F8: submitFloor also rejects a.fromBlock > a.toBlock or a.toBlock + 7800 < block.number.
- F9: record reserveUsed[id] at settlement; receiveRecovery(id) restores min(value, reserveUsed[id]) to the shortfall reserve before vesting the rest.
- F14: buyAuction rejects receiver == loan.vault or == loan.collection.
- F15: after any questionHash write to a collection, including a queued rotation, that collection's new loans are disabled for 48 hours. The constructor preset is exempt.
- F16: wrap IDiscountModule.release in try/catch on repay, buyAuction and writeOffAuction.
- Protocol share: while either reserve is below target, 50% of each protocol fee goes to the reserves and 50% to the fee recipient; once both are at target, 100% to the fee recipient. The lenders' 85% is unchanged.
LENDINGPOOL
- F4: in settleAuction, when the released loss allowance exceeds the realised loss, vest the difference over 7 days instead of releasing it at once; markAuctionLoss may lower the allowance while holdsCollateral() is true.
- F12: executeDepositCap gets the same 7-day execution window as PawnShop changes, plus onlyOwner cancelDepositCap().
COLLATERALVAULT (implementation redeployed with the shop): F13: isValidSignature uses holdsCollateral() and returns 0xffffffff instead of reverting.
MILESTONEBURN: F11: accepted attestation age at most 1 hour; questionHash settable once as before; README notes the question must use a 24-hour time-weighted price.
TESTS: F10 fix the invariant formula (additions first); add the audit's proof tests for F1, F3, F4 to the suite; a test per fix above, including the protocol-share split.
SITE: update all addresses to the new contracts; pawn passes minPrincipal and maxFee; Setup shows the preset hash and attester as done and drops the attester-switch step; Borrow and Setup use the FloorRelay request-id flow; Stats shows protocol fees paid to the recipient and reserve levels. Build and publish under the same name.
DOCS: README and docs/SETUP-AND-KEEPER.md updated; list the audit findings and which fix addresses each.