Preview opening a Trove. A refusal is a verdict with reasons, never an error: every gate the
contract would apply comes back in reasons. What it does throw is a failure to READ the state
the verdict needs, and that arrives typed (MK-105): OracleStale when the price feed refuses to
answer (PriceFeed.sol:51-54), and ContractCallFailed for a transport failure.
Mirrors _openTrove (BorrowerOperations.sol:631-665) rather than approximating it:
The fee is charged only when !isRecoveryMode && !isAccountFeeExempt(borrower)
(:637-643). In Recovery Mode, or for an exempt account, netDebt is the bare draw
and no fee is charged at all. The old version applied getBorrowingFee
unconditionally while separately reporting isRecoveryMode in the same result
(MK-004, MK-018).
The debt floor is _requireAtLeastMinNetDebt(vars.netDebt) (:645), checked
against that same netDebt. This is the second order effect worth naming: because
the old version added a fee the contract would not charge, in the band where
draw < minNetDebt <= draw + fee it reported the floor MET for an open that reverts.
Charging the fee correctly closes that band.
The Trove must not already be active (:633, :1140-1149), which is the FIRST
gate and was missing entirely until MK-047: an owner who already held a Trove got
viable: true while the contract refused with BorrowerOps: Trove is active. It is
evaluated only when account is supplied, because without one there is nobody to ask.
The ratios: in Recovery Mode the contract requires ICR >= CCR (:654-655); in
normal mode it requires BOTH ICR >= MCR and a resulting system TCR >= CCR
(:656-665). The old meetsRecoveryRequirement was !isRecoveryMode || icr >= CCR,
which is unconditionally true in normal mode, so it never checked the ICR floor and
never projected the TCR at all (MK-005).
Every raw number is still returned so a caller can render its own message.
Not a single block snapshot: the price is read outside the batch that uses it. The full
statement is on MathDeps in math/deps.ts (MK-013, MK-093).
Preview opening a Trove. A refusal is a verdict with reasons, never an error: every gate the contract would apply comes back in
reasons. What it does throw is a failure to READ the state the verdict needs, and that arrives typed (MK-105):OracleStalewhen the price feed refuses to answer (PriceFeed.sol:51-54), andContractCallFailedfor a transport failure.Mirrors
_openTrove(BorrowerOperations.sol:631-665) rather than approximating it:!isRecoveryMode && !isAccountFeeExempt(borrower)(:637-643). In Recovery Mode, or for an exempt account,netDebtis the bare draw and no fee is charged at all. The old version appliedgetBorrowingFeeunconditionally while separately reportingisRecoveryModein the same result (MK-004, MK-018)._requireAtLeastMinNetDebt(vars.netDebt)(:645), checked against that samenetDebt. This is the second order effect worth naming: because the old version added a fee the contract would not charge, in the band wheredraw < minNetDebt <= draw + feeit reported the floor MET for an open that reverts. Charging the fee correctly closes that band.:633,:1140-1149), which is the FIRST gate and was missing entirely until MK-047: an owner who already held a Trove gotviable: truewhile the contract refused withBorrowerOps: Trove is active. It is evaluated only whenaccountis supplied, because without one there is nobody to ask.ICR >= CCR(:654-655); in normal mode it requires BOTHICR >= MCRand a resulting systemTCR >= CCR(:656-665). The oldmeetsRecoveryRequirementwas!isRecoveryMode || icr >= CCR, which is unconditionallytruein normal mode, so it never checked the ICR floor and never projected the TCR at all (MK-005).Every raw number is still returned so a caller can render its own message.
Not a single block snapshot: the price is read outside the batch that uses it. The full statement is on
MathDepsinmath/deps.ts(MK-013, MK-093).