diff --git a/README.md b/README.md index fe2b019..52ccbc5 100644 --- a/README.md +++ b/README.md @@ -79,13 +79,13 @@ Below is an outline of all functions used in the library. - `pot()`: Savings Rates Module - `vow()`: System Stabilizer Module - `end()`: Shutdown Coordinator +- `esm()`: Emergency Shutdown Module - `reg()`: Ilk Registry - `spotter()`: Oracle Liason - `flap()`: Surplus Auction Module - `flop()`: Debt Auction Module - `osmMom()`: OSM Circuit Breaker -- `govGuard()`: MKR Authority -- `flipperMom()`: Flipper Governance Interface +- `govGuard()`: SKY Authority - `clipperMom()`: Clipper Governance Interface (Liquidations 2.0) - `pauseProxy()`: Governance Authority - `autoLine()`: Debt Ceiling Auto Adjustment @@ -135,6 +135,8 @@ Below is an outline of all functions used in the library. - `setContract(address _base, bytes32 _what, address _addr)`: Set a contract in another contract, defining the relationship (ex. set a new Vow contract in the Dog) - `setContract(address _base, bytes32 _ilk, bytes32 _what, address _addr)`: Set a contract in another contract, defining the relationship for a given ilk. +- `setValue(address _base, bytes32 _what, uint256 _amt)`: Set a value in a contract via a governance authorized File pattern. +- `setValue(address _base, bytes32 _ilk, bytes32 _what, uint256 _amt)`: Set an ilk-specific value in a contract via a governance authorized File pattern. ### System Risk Parameters @@ -144,7 +146,7 @@ Below is an outline of all functions used in the library. - `setDSR(uint256 _rate, bool _doDrip)`: Set the Dai Savings Rate. - `setSSR(uint256 _rate, bool _doDrip)`: Set the Sky Savings Rate. - `setSurplusAuctionAmount(uint256 _amount)`: Set the amount for system surplus auctions. -- `setSurplusAuctionMinPriceThreshold(uint256 _pct_bps)`: Set the relative multiplier of the reference price to insist in the swap. For example, `9_80` bps allows a 2% drop in the reference price. +- `setSurplusAuctionMinPriceThreshold(uint256 _pct_bps)`: Set the relative multiplier of the reference price to insist in the swap. For example, `98_00` bps allows a 2% drop in the reference price. - `setSurplusBuffer(uint256 _amount)`: Set the amount for system surplus buffer, must be exceeded before surplus auctions start. - `setDebtAuctionDelay(uint256 _length)`: Set the number of seconds that pass before system debt is auctioned for MKR tokens. - `setDebtAuctionDebtAmount(uint256 _amount)`: Set the debt amount for system debt to be covered by each debt auction. @@ -182,9 +184,9 @@ Below is an outline of all functions used in the library. ### Abacus Management -- `initLinearDecrease(address _calc, uint256 _duration)`: Initialize the variables in a LinearDecrease calculator. -- `initStairstepExponentialDecrease(address _calc, uint256 _duration, uint256 _pct_bps)`: Initialize the variables in a StairstepExponentialDecrease calculator. -- `initExponentialDecrease(address _calc, uint256 _pct_bps)`: Initialize the variables in an ExponentialDecrease calculator. +- `setLinearDecrease(address _calc, uint256 _duration)`: Set the variables in a LinearDecrease calculator. +- `setStairstepExponentialDecrease(address _calc, uint256 _duration, uint256 _pct_bps)`: Set the variables in a StairstepExponentialDecrease calculator. +- `setExponentialDecrease(address _calc, uint256 _pct_bps)`: Set the variables in an ExponentialDecrease calculator. ### Oracle Management @@ -227,9 +229,9 @@ Once these actions are done, add the following code (below is an example) to the ```solidity import "src/CollateralOpts.sol"; -// Initialize the pricing function with the appropriate initializer +// Set the pricing function with the appropriate setter address xmpl_calc = 0x1f206d7916Fd3B1b5B0Ce53d5Cab11FCebc124DA; -DssExecLib.initStairstepExponentialDecrease(xmpl_calc, 60, 9900); +DssExecLib.setStairstepExponentialDecrease(xmpl_calc, 60, 9900); CollateralOpts memory XMPL_A = CollateralOpts({ ilk: "XMPL-A", @@ -246,9 +248,12 @@ CollateralOpts memory XMPL_A = CollateralOpts({ liquidationPenalty: 1300, // 13% penalty ilkStabilityFee: 1000000000705562181084137268, startingPriceFactor: 13000, // 1.3x multiplier + breakerTolerance: 9500, // 95% price tolerance auctionDuration: 6 hours, permittedDrop: 4000, // 40% drop before reset - liquidationRatio: 15000 // 150% collateralization ratio + liquidationRatio: 15000, // 150% collateralization ratio + kprFlatReward: 300, // 300 Dai flat reward + kprPctReward: 10 // 0.1% percentage reward }); DssExecLib.addNewCollateral(XMPL_A); @@ -275,14 +280,22 @@ DssExecLib.setChangelogAddress("MCD_CLIP_CALC_XMPL-A", xmpl_calc); - `liquidationPenalty`: Percent liquidation penalty for new collateral [ex. 13.5% == 1350] - `ilkStabilityFee`: Percent stability fee for new collateral [ex. 4% == 1000000001243680656318820312] - `startingPriceFactor`: Percentage to multiply for initial auction price. [ex. 1.3x == 130% == 13000 bps] +- `breakerTolerance`: Percentage tolerance for liquidation circuit breaker [ex. 95% == 9500 bps] - `auctionDuration`: Total auction duration before reset for new collateral - `permittedDrop`: Percent an auction can drop before it can be reset. - `liquidationRatio`: Percent liquidation ratio for new collateral [ex. 150% == 15000] +- `kprFlatReward`: Flat DAI reward paid to keepers for liquidation actions [ex. 300 DAI == 300] +- `kprPctReward`: Percentage of liquidation amount paid to keepers [ex. 0.1% == 10 bps] ### Payments - `sendPaymentFromSurplusBuffer(address _join, address _target, uint256 _amount)`: Send a payment in either ERC20 USDS or DAI from the surplus buffer. +### SubDAO/Star Spells + +- `executeStarSpell(address _starProxy, address _starSpell)`: Execute a star spell through its star proxy. Returns the return data from the spell execution. +- `tryExecuteStarSpell(address _starProxy, address _starSpell)`: Tries to execute a star spell through its star proxy using low-level call to avoid reverts in case of error. Returns a boolean indicating success and the return data or error message. + ### Misc - `function linearInterpolation(bytes32 _name, address _target, bytes32 _what, uint256 _startTime, uint256 _start, uint256 _end, uint256 _duration)`: Deploys a new general lerp contract with the associated parameters and returns the `address` of the contract. diff --git a/audits/ChainSecurity_Sky_DSS_Exec_Lib_audit_2025-09-12.pdf b/audits/ChainSecurity_Sky_DSS_Exec_Lib_audit_2025-09-12.pdf new file mode 100644 index 0000000..2b71e17 Binary files /dev/null and b/audits/ChainSecurity_Sky_DSS_Exec_Lib_audit_2025-09-12.pdf differ diff --git a/audits/cantina_sky_dss_exec_lib_jan_2026-01-29.pdf b/audits/cantina_sky_dss_exec_lib_jan_2026-01-29.pdf new file mode 100644 index 0000000..4d8aaf8 Binary files /dev/null and b/audits/cantina_sky_dss_exec_lib_jan_2026-01-29.pdf differ diff --git a/src/DssAction.t.sol b/src/DssAction.t.sol index a4a7eee..136028c 100644 --- a/src/DssAction.t.sol +++ b/src/DssAction.t.sol @@ -157,7 +157,7 @@ contract DssActionTest is Test { address constant UNIV2ORACLE_FAB = 0xc968B955BCA6c2a3c828d699cCaCbFDC02402D89; function setUp() public { - vm.createSelectFork("apr_15_0"); + vm.createSelectFork("mainnet"); START_TIME = block.timestamp; @@ -348,9 +348,8 @@ contract DssActionTest is Test { vat.rely(address(join)); ClipAbstract clip = ClipAbstract( - ClipFabLike(LOG.getAddress("CLIP_FAB")).newClip( - address(this), address(vat), address(spot), address(dog), name - ) + ClipFabLike(LOG.getAddress("CLIP_FAB")) + .newClip(address(this), address(vat), address(spot), address(dog), name) ); vat.hope(address(clip)); clip.rely(address(end)); @@ -766,6 +765,11 @@ contract DssActionTest is Test { assertEq(uint256(ttl), initialTtl); // ttl should remain unchanged } + function test_RevertSetIlkAutoLineParametersKeepTtl_WhenNotConfigured() public { + vm.expectRevert(); + action.setIlkAutoLineParameters_test("gold", 200 * MILLION, 10 * MILLION); + } + function test_setIlkAutoLineDebtCeiling() public { action.setIlkAutoLineParameters_test("gold", 1, 5 * MILLION, 10000); // gap and ttl must be configured already action.setIlkAutoLineDebtCeiling_test("gold", 150 * MILLION); // Setup @@ -798,8 +802,8 @@ contract DssActionTest is Test { (,,,, uint256 dust) = vat.ilks("gold"); assertEq(dust, 100 * RAD); - action.setIlkMaxLiquidationAmount_test("gold", 0); action.setIlkMinVaultAmount_test("gold", 0); + action.setIlkMaxLiquidationAmount_test("gold", 0); (,,,, dust) = vat.ilks("gold"); assertEq(dust, 0); } @@ -822,6 +826,13 @@ contract DssActionTest is Test { assertEq(hole, 50 * THOUSAND * RAD); } + function test_setIlkMaxLiquidationAmountLtReverts() public { + action.setIlkMaxLiquidationAmount_test("gold", 100); + action.setIlkMinVaultAmount_test("gold", 100); + vm.expectRevert(); + action.setIlkMaxLiquidationAmount_test("gold", 99); + } + function test_setIlkLiquidationRatio() public { action.setIlkLiquidationRatio_test("gold", 15000); // 150% in bp (, uint256 mat) = spot.ilks("gold"); @@ -960,9 +971,8 @@ contract DssActionTest is Test { GemJoinAbstract tokenJoin = GemJoinAbstract(GemJoinFabLike(LOG.getAddress("JOIN_FAB")).newGemJoin(address(this), ilk, address(token))); ClipAbstract tokenClip = ClipAbstract( - ClipFabLike(LOG.getAddress("CLIP_FAB")).newClip( - address(this), address(vat), address(spot), address(dog), ilk - ) + ClipFabLike(LOG.getAddress("CLIP_FAB")) + .newClip(address(this), address(vat), address(spot), address(dog), ilk) ); LinearDecreaseAbstract tokenCalc = LinearDecreaseAbstract(CalcFabLike(LOG.getAddress("CALC_FAB")).newLinearDecrease(address(this))); @@ -1004,9 +1014,8 @@ contract DssActionTest is Test { GemJoinAbstract tokenJoin = GemJoinAbstract(GemJoinFabLike(LOG.getAddress("JOIN_FAB")).newGemJoin(address(this), ilk, address(token))); ClipAbstract tokenClip = ClipAbstract( - ClipFabLike(LOG.getAddress("CLIP_FAB")).newClip( - address(this), address(vat), address(spot), address(dog), ilk - ) + ClipFabLike(LOG.getAddress("CLIP_FAB")) + .newClip(address(this), address(vat), address(spot), address(dog), ilk) ); LinearDecreaseAbstract tokenCalc = LinearDecreaseAbstract(CalcFabLike(LOG.getAddress("CALC_FAB")).newLinearDecrease(address(this))); diff --git a/src/DssExec.t.sol b/src/DssExec.t.sol index 77ae20e..184a096 100644 --- a/src/DssExec.t.sol +++ b/src/DssExec.t.sol @@ -71,9 +71,8 @@ contract DssLibSpellAction is // Basic cob setup DSTokenAbstract xmpl_gem = DSTokenAbstract(0xCE4F3774620764Ea881a8F8840Cbe0F701372283); ClipAbstract xmpl_clip = ClipAbstract( - ClipFabLike(LOG.getAddress("CLIP_FAB")).newClip( - DssExecLib.pauseProxy(), DssExecLib.vat(), DssExecLib.spotter(), DssExecLib.dog(), "XMPL-A" - ) + ClipFabLike(LOG.getAddress("CLIP_FAB")) + .newClip(DssExecLib.pauseProxy(), DssExecLib.vat(), DssExecLib.spotter(), DssExecLib.dog(), "XMPL-A") ); GemJoinAbstract xmpl_join = GemJoinAbstract( GemJoinFabLike(LOG.getAddress("JOIN_FAB")).newGemJoin(address(this), "XMPL-A", address(xmpl_gem)) @@ -84,7 +83,7 @@ contract DssLibSpellAction is LinearDecreaseAbstract xmpl_calc = LinearDecreaseAbstract(CalcFabLike(LOG.getAddress("CALC_FAB")).newLinearDecrease(address(this))); - DssExecLib.setLinearDecrease(address(xmpl_calc), 1); + DssExecLib.setLinearDecrease(address(xmpl_calc), 6 hours); CollateralOpts memory XMPL_A = CollateralOpts({ ilk: "XMPL-A", @@ -112,14 +111,14 @@ contract DssLibSpellAction is DssExecLib.addNewCollateral(XMPL_A); - DssExecLib.setIlkDebtCeiling("LINK-A", 10 * MILLION); - DssExecLib.setIlkMinVaultAmount("LINK-A", 800); - DssExecLib.setIlkLiquidationRatio("LINK-A", 16000); - DssExecLib.setIlkLiquidationPenalty("LINK-A", 1400); - DssExecLib.setIlkMaxLiquidationAmount("LINK-A", 100000); - DssExecLib.setAuctionTimeBeforeReset("LINK-A", 2 hours); - DssExecLib.setKeeperIncentivePercent("LINK-A", 2); // 0.02% keeper incentive - DssExecLib.setGlobalDebtCeiling(10000 * MILLION); + DssExecLib.setIlkDebtCeiling("ETH-A", 10 * MILLION); + DssExecLib.setIlkMinVaultAmount("ETH-A", 800); + DssExecLib.setIlkLiquidationRatio("ETH-A", 16000); + DssExecLib.setIlkLiquidationPenalty("ETH-A", 1400); + DssExecLib.setIlkMaxLiquidationAmount("ETH-A", 100000); + DssExecLib.setAuctionTimeBeforeReset("ETH-A", 2 hours); + DssExecLib.setKeeperIncentivePercent("ETH-A", 2); // 0.02% keeper incentive + DssExecLib.increaseGlobalDebtCeiling(10000 * MILLION); } } @@ -214,7 +213,7 @@ contract DssExecTest is Test { osmMom = OsmMomAbstract(LOG.getAddress("OSM_MOM")); clipMom = ClipperMomAbstract(LOG.getAddress("CLIPPER_MOM")); xmpl = GemAbstract(0xCE4F3774620764Ea881a8F8840Cbe0F701372283); - pipXMPL = OsmAbstract(LOG.getAddress("PIP_USDT")); + pipXMPL = OsmAbstract(0x7a5918670B0C390aD25f7beE908c1ACc2d314A3C); rates = new MockRates(); @@ -226,7 +225,7 @@ contract DssExecTest is Test { // Test for all system configuration changes afterSpell.dsr_rate = 0; // In basis points - afterSpell.vat_Line = 10000 * MILLION; // In whole Dai units + afterSpell.vat_Line = vat.Line() / RAD + 10000 * MILLION + 3 * MILLION; // In whole Dai units afterSpell.pause_delay = pause.delay(); // In seconds afterSpell.vow_wait = vow.wait(); // In seconds afterSpell.vow_dump = vow.dump() / WAD; // In whole Dai units @@ -237,10 +236,10 @@ contract DssExecTest is Test { afterSpell.ilk_count = reg.count() + 1; // Num expected in system // Test for all collateral based changes here - (uint256 _duty,) = jug.ilks("LINK-A"); - (address _clip,,,) = dog.ilks("LINK-A"); + (uint256 _duty,) = jug.ilks("ETH-A"); + (address _clip,,,) = dog.ilks("ETH-A"); ClipAbstract clip = ClipAbstract(_clip); - afterSpell.collaterals["LINK-A"] = CollateralValues({ + afterSpell.collaterals["ETH-A"] = CollateralValues({ line: 10 * MILLION, // In whole Dai units dust: 800, // In whole Dai units pct: _duty, // In basis points @@ -321,10 +320,6 @@ contract DssExecTest is Test { return (10000 + percentValue) * (10 ** 23); } - function diffCalc(uint256 expectedRate_, uint256 yearlyYield_) public pure returns (uint256) { - return (expectedRate_ > yearlyYield_) ? expectedRate_ - yearlyYield_ : yearlyYield_ - expectedRate_; - } - function ray(uint256 wad) internal pure returns (uint256) { return wad * 10 ** 9; } @@ -338,9 +333,8 @@ contract DssExecTest is Test { stdstore.target(address(chief)).sig("live()").checked_write(bytes32(uint256(1))); } if (chief.hat() != address(spell)) { - stdstore.target(address(gov)).sig("balanceOf(address)").with_key(address(this)).checked_write( - bytes32(uint256(999999999999 ether)) - ); + stdstore.target(address(gov)).sig("balanceOf(address)").with_key(address(this)) + .checked_write(bytes32(uint256(999999999999 ether))); gov.approve(address(chief), type(uint256).max); chief.lock(gov.balanceOf(address(this)) - 1 ether); @@ -427,12 +421,12 @@ contract DssExecTest is Test { // bc -l <<< 'scale=27; e( l(2.00)/(60 * 60 * 24 * 365) )' // 1000000021979553151239153027 assertTrue(pot.dsr() >= RAY && pot.dsr() < 1000000021979553151239153027); - assertTrue(diffCalc(expectedRate(values.dsr_rate), yearlyYield(expectedDSRRate)) <= TOLERANCE); + assertApproxEqAbs(expectedRate(values.dsr_rate), yearlyYield(expectedDSRRate), TOLERANCE); { // Line values in RAD uint256 normalizedLine = values.vat_Line * RAD; - assertEq(vat.Line(), normalizedLine); + assertApproxEqAbs(vat.Line(), normalizedLine, RAD - 1); assertTrue((vat.Line() >= RAD && vat.Line() < 100 * BILLION * RAD) || vat.Line() == 0); } @@ -458,20 +452,21 @@ contract DssExecTest is Test { assertTrue((vow.sump() >= RAD && vow.sump() < 500 * THOUSAND * RAD) || vow.sump() == 0); } } - { - // bump values in RAD - uint256 normalizedBump = values.vow_bump * RAD; - assertEq(vow.bump(), normalizedBump); - assertTrue((vow.bump() >= RAD && vow.bump() < HUNDRED * THOUSAND * RAD) || vow.bump() == 0); - } - { - // hump values in RAD - assertEq(vow.hump() / RAD, values.vow_hump); - assertTrue( - (vow.hump() >= RAD && vow.hump() < THOUSAND * MILLION * RAD) || vow.hump() == 0, - "DssExec.t.sol/hump-sanity-check-fail" - ); - } + // Flapper auctions have been disabled in favor of the Kicker + // { + // // bump values in RAD + // uint256 normalizedBump = values.vow_bump * RAD; + // assertEq(vow.bump(), normalizedBump); + // assertTrue((vow.bump() >= RAD && vow.bump() < HUNDRED * THOUSAND * RAD) || vow.bump() == 0); + // } + // { + // // hump values in RAD + // assertEq(vow.hump() / RAD, values.vow_hump); + // assertTrue( + // (vow.hump() >= RAD && vow.hump() < THOUSAND * MILLION * RAD) || vow.hump() == 0, + // "DssExec.t.sol/hump-sanity-check-fail" + // ); + // } // Hole values in RAD { @@ -494,10 +489,10 @@ contract DssExecTest is Test { // basis points used assertTrue(values.collaterals[ilk].pct < THOUSAND * THOUSAND); // check value lt 1000% normRate = rates.rates(values.collaterals[ilk].pct); - assertTrue( - diffCalc( - expectedRate(values.collaterals[ilk].pct), yearlyYield(rates.rates(values.collaterals[ilk].pct)) - ) <= TOLERANCE + assertApproxEqAbs( + expectedRate(values.collaterals[ilk].pct), + yearlyYield(rates.rates(values.collaterals[ilk].pct)), + TOLERANCE ); } @@ -579,7 +574,7 @@ contract DssExecTest is Test { assertTrue(spell.done()); checkSystemValues(afterSpell); - checkCollateralValues("LINK-A", afterSpell); + checkCollateralValues("ETH-A", afterSpell); checkCollateralValues("XMPL-A", afterSpell); assertTrue(spell.officeHours()); @@ -602,6 +597,18 @@ contract DssExecTest is Test { clipXMPLA = ClipAbstract(clip); joinXMPLA = GemJoinAbstract(reg.join("XMPL-A")); + // Wiring + (address spotPip,) = spot.ilks("XMPL-A"); + assertEq(spotPip, address(pipXMPL)); + assertEq(reg.class("XMPL-A"), 1); + assertEq(reg.gem("XMPL-A"), address(xmpl)); + assertEq(reg.pip("XMPL-A"), address(pipXMPL)); + assertEq(reg.xlip("XMPL-A"), address(clipXMPLA)); + assertEq(reg.dec("XMPL-A"), uint256(xmpl.decimals())); + assertEq(clipXMPLA.vow(), address(vow)); + assertTrue(clipXMPLA.calc() != address(0)); + assertEq(LinearDecreaseAbstract(clipXMPLA.calc()).tau(), afterSpell.collaterals["XMPL-A"].tau); + // Authorization assertEq(joinXMPLA.wards(pauseProxy), 1); assertEq(vat.wards(address(joinXMPLA)), 1); @@ -612,6 +619,8 @@ contract DssExecTest is Test { assertEq(clipXMPLA.wards(address(clipMom)), 1); assertEq(pipXMPL.wards(address(osmMom)), 1); assertEq(pipXMPL.bud(address(spot)), 1); + assertEq(pipXMPL.bud(address(clipXMPLA)), 1); + assertEq(pipXMPL.bud(address(clipMom)), 1); assertEq(pipXMPL.bud(address(end)), 1); assertEq(MedianAbstract(pipXMPL.src()).bud(address(pipXMPL)), 1); diff --git a/src/DssExecLib.sol b/src/DssExecLib.sol index 07b92ec..12cb5b6 100644 --- a/src/DssExecLib.sol +++ b/src/DssExecLib.sol @@ -183,7 +183,7 @@ library DssExecLib { /// @dev RAY division. The final result is rounded to the nearest integer. /// Examples: - /// rdiv(1, 2) = 0.5 * WAD = 500000000000000000000000000 + /// rdiv(1, 2) = 0.5 * RAY = 500000000000000000000000000 /// rdiv(2, 3) = 0.666... * RAY = 666666666666666666666666667 function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) { z = (x * RAY + y / 2) / y; @@ -191,118 +191,173 @@ library DssExecLib { /* ----- Core Address Helpers ----- */ + /// @notice Get the DAI token contract address from the changelog + /// @return The address of the DAI token contract function dai() public view returns (address) { return getChangelogAddress("MCD_DAI"); } + /// @notice Get the USDS token contract address from the changelog + /// @return The address of the USDS token contract function usds() public view returns (address) { return getChangelogAddress("USDS"); } + /// @notice Get the MKR token contract address from the changelog + /// @return The address of the MKR token contract function mkr() public view returns (address) { return getChangelogAddress("MKR"); } + /// @notice Get the SKY token contract address from the changelog + /// @return The address of the SKY token contract function sky() public view returns (address) { return getChangelogAddress("SKY"); } + /// @notice Get the VAT (accounting core) contract address from the changelog + /// @return The address of the VAT contract function vat() public view returns (address) { return getChangelogAddress("MCD_VAT"); } + /// @notice Get the DOG (liquidation module) contract address from the changelog + /// @return The address of the DOG contract function dog() public view returns (address) { return getChangelogAddress("MCD_DOG"); } + /// @notice Get the JUG (stability fee collector) contract address from the changelog + /// @return The address of the JUG contract function jug() public view returns (address) { return getChangelogAddress("MCD_JUG"); } + /// @notice Get the POT (DAI Savings Rate) contract address from the changelog + /// @return The address of the POT contract function pot() public view returns (address) { return getChangelogAddress("MCD_POT"); } + /// @notice Get the SUSDS (SKY Savings Rate) contract address from the changelog + /// @return The address of the SUSDS contract function susds() public view returns (address) { return getChangelogAddress("SUSDS"); } + /// @notice Get the VOW (system surplus and debt manager) contract address from the changelog + /// @return The address of the VOW contract function vow() public view returns (address) { return getChangelogAddress("MCD_VOW"); } + /// @notice Get the END (emergency shutdown) contract address from the changelog + /// @return The address of the END contract function end() public view returns (address) { return getChangelogAddress("MCD_END"); } + /// @notice Get the ESM (emergency shutdown module) contract address from the changelog + /// @return The address of the ESM contract function esm() public view returns (address) { return getChangelogAddress("MCD_ESM"); } + /// @notice Get the ILK_REGISTRY (collateral registry) contract address from the changelog + /// @return The address of the ILK_REGISTRY contract function reg() public view returns (address) { return getChangelogAddress("ILK_REGISTRY"); } + /// @notice Get the SPOTTER (price feed interface) contract address from the changelog + /// @return The address of the SPOTTER contract function spotter() public view returns (address) { return getChangelogAddress("MCD_SPOT"); } + /// @notice Get the FLAP (surplus auction) contract address from the changelog + /// @return The address of the FLAP contract function flap() public view returns (address) { return getChangelogAddress("MCD_FLAP"); } + /// @notice Get the FLOP (debt auction) contract address from the changelog + /// @return The address of the FLOP contract function flop() public view returns (address) { return getChangelogAddress("MCD_FLOP"); } + /// @notice Get the OSM_MOM (oracle security module mom) contract address from the changelog + /// @return The address of the OSM_MOM contract function osmMom() public view returns (address) { return getChangelogAddress("OSM_MOM"); } - function mkrGuard() public view returns (address) { - return getChangelogAddress("MKR_GUARD"); - } - - function flipperMom() public view returns (address) { - return getChangelogAddress("FLIPPER_MOM"); + /// @notice Get the GOV_GUARD (governance guard) contract address from the changelog + /// @return The address of the GOV_GUARD contract + function govGuard() public view returns (address) { + return getChangelogAddress("GOV_GUARD"); } + /// @notice Get the CLIPPER_MOM (liquidation circuit breaker) contract address from the changelog + /// @return The address of the CLIPPER_MOM contract function clipperMom() public view returns (address) { return getChangelogAddress("CLIPPER_MOM"); } + /// @notice Get the PAUSE_PROXY (governance proxy) contract address from the changelog + /// @return The address of the PAUSE_PROXY contract function pauseProxy() public view returns (address) { return getChangelogAddress("MCD_PAUSE_PROXY"); } + /// @notice Get the IAM_AUTO_LINE (auto debt ceiling adjuster) contract address from the changelog + /// @return The address of the IAM_AUTO_LINE contract function autoLine() public view returns (address) { return getChangelogAddress("MCD_IAM_AUTO_LINE"); } + /// @notice Get the DAI_JOIN (DAI token adapter) contract address from the changelog + /// @return The address of the DAI_JOIN contract function daiJoin() public view returns (address) { return getChangelogAddress("MCD_JOIN_DAI"); } + /// @notice Get the USDS_JOIN (USDS token adapter) contract address from the changelog + /// @return The address of the USDS_JOIN contract function usdsJoin() public view returns (address) { return getChangelogAddress("USDS_JOIN"); } + /// @notice Get the LERP_FAB (linear interpolation factory) contract address from the changelog + /// @return The address of the LERP_FAB contract function lerpFab() public view returns (address) { return getChangelogAddress("LERP_FAB"); } + /// @notice Get the PAUSE (governance delay) contract address from the changelog + /// @return The address of the PAUSE contract function pause() public view returns (address) { return getChangelogAddress("MCD_PAUSE"); } + /// @notice Get the collateral liquidation contract address for a given ilk + /// @param _ilk The collateral type identifier + /// @return _clip The address of the liquidation contract for the given ilk function clip(bytes32 _ilk) public view returns (address _clip) { _clip = RegistryLike(reg()).xlip(_ilk); } + /// @notice Get the collateral auction contract address for a given ilk (legacy) + /// @param _ilk The collateral type identifier + /// @return _flip The address of the auction contract for the given ilk function flip(bytes32 _ilk) public view returns (address _flip) { _flip = RegistryLike(reg()).xlip(_ilk); } + /// @notice Get the pricing calculator contract address for a given ilk + /// @param _ilk The collateral type identifier + /// @return _calc The address of the pricing calculator contract for the given ilk function calc(bytes32 _ilk) public view returns (address _calc) { _calc = ClipLike(clip(_ilk)).calc(); } @@ -378,9 +433,9 @@ library DssExecLib { Kissable(_target).diss(_usr); } - /// @dev Give an address authorization to perform auth actions on the contract. - /// @param _base The address of the contract with a `setAuthority` pattern - /// @param _authority Address to be authorized + /// @dev Set the authority contract that manages access control for the target contract. + /// @param _base The address of the contract where the authority will be set + /// @param _authority Address of the authority contract that will manage privileged access (e.g., Chief managing who can call the Pause contract) function setAuthority(address _base, address _authority) public { Authorizable(_base).setAuthority(_authority); } @@ -493,7 +548,7 @@ library DssExecLib { /// @dev Set a value in a contract, via a governance authorized File pattern. /// @param _base The address of the contract where the new contract address will be filed - /// @param _what Name of tag for the value (e.x. "Line") + /// @param _what Name of tag for the value (e.g. "Line") /// @param _amt The value to set or update function setValue(address _base, bytes32 _what, uint256 _amt) public { Fileable(_base).file(_what, _amt); @@ -502,7 +557,7 @@ library DssExecLib { /// @dev Set an ilk-specific value in a contract, via a governance authorized File pattern. /// @param _base The address of the contract where the new value will be filed /// @param _ilk Collateral type - /// @param _what Name of tag for the value (e.x. "Line") + /// @param _what Name of tag for the value (e.g. "Line") /// @param _amt The value to set or update function setValue(address _base, bytes32 _ilk, bytes32 _what, uint256 _amt) public { Fileable(_base).file(_ilk, _what, _amt); @@ -695,7 +750,7 @@ library DssExecLib { } /// @dev Set a RWA collateral debt ceiling by specifying its new oracle price. - /// @param _ilk The ilk to update (ex. bytes32("ETH-A")) + /// @param _ilk The ilk to update (ex. bytes32("RWA001-A")) /// @param _ceiling The new debt ceiling in natural units (e.g. set 10m as 10_000_000) /// @param _price The new oracle price in natural units /// @dev note: currently only DAI is supported in RWA vaults. @@ -721,14 +776,16 @@ library DssExecLib { } /// @dev Set the parameters for an ilk in the "MCD_IAM_AUTO_LINE" auto-line. Keeps the ttl unchanged. + /// Requires the auto-line to be already configured for the ilk. /// @param _ilk The ilk to update (ex. bytes32("ETH-A")) /// @param _amount The Maximum value (ex. 100m amount == 100000000) /// @param _gap The amount per step (ex. 5m gap == 5000000) function setIlkAutoLineParameters(bytes32 _ilk, uint256 _amount, uint256 _gap) public { - address _autoLine = autoLine(); - (,, uint48 ttl,,) = IAMLike(_autoLine).ilks(_ilk); require(_amount < WAD); // "LibDssExec/incorrect-auto-line-amount-precision" require(_gap < WAD); // "LibDssExec/incorrect-auto-line-gap-precision" + address _autoLine = autoLine(); + (,, uint48 ttl,,) = IAMLike(_autoLine).ilks(_ilk); + require(ttl != 0); // "LibDssExec/auto-line-not-configured" IAMLike(_autoLine).setIlk(_ilk, _amount * RAD, _gap * RAD, uint256(ttl)); } @@ -774,6 +831,8 @@ library DssExecLib { /// @param _amount The amount to set (ex. 10m amount == 10000000) function setIlkMaxLiquidationAmount(bytes32 _ilk, uint256 _amount) public { require(_amount < WAD); // "LibDssExec/incorrect-ilk-hole-precision" + (,,,, uint256 _dust) = DssVat(vat()).ilks(_ilk); + require(_amount * RAD >= _dust); // Ensure hole >= ilk.dust setValue(dog(), _ilk, "hole", _amount * RAD); } @@ -820,11 +879,12 @@ library DssExecLib { setValue(clip(_ilk), "chip", wdiv(_pct_bps, BPS_ONE_HUNDRED_PCT)); } - /// @dev Set max amount for flat rate keeper incentive. Amount will be converted to the correct internal precision. + /// @dev Sets the amount for flat rate keeper incentive. Amount will be converted to the correct internal precision. /// @param _ilk The ilk to update (ex. bytes32("ETH-A")) /// @param _amount The amount to set (ex. 1000 amount == 1000) function setKeeperIncentiveFlatRate(bytes32 _ilk, uint256 _amount) public { require(_amount < WAD); // "LibDssExec/incorrect-clip-tip-precision" + require(_amount * RAD <= type(uint192).max); // "LibDssExec/clip-tip-precision-overflow" setValue(clip(_ilk), "tip", _amount * RAD); } @@ -922,7 +982,10 @@ library DssExecLib { /* ----- Collateral Onboarding ----- */ - /// @dev Performs basic functions and sanity checks to add a new collateral type to the Sky Protocol + /// @dev Performs basic collateral setup with core contract integrations and authorizations. + /// @dev This function handles the fundamental integration of a new collateral type into the core Sky Protocol + /// contracts (VAT, DOG, JUG, etc.) without setting risk parameters. Use this when you need basic setup + /// without full parameter configuration, or as a building block for more complex onboarding. /// @param _ilk Collateral type key code [Ex. "ETH-A"] /// @param _gem Address of token contract /// @param _join Address of join adapter @@ -978,7 +1041,10 @@ library DssExecLib { RegistryLike(reg()).add(_join); } - // Complete collateral onboarding logic. + /// @dev Complete collateral onboarding with all necessary configurations and authorizations. + /// @dev This function performs comprehensive collateral setup including debt ceilings, liquidation parameters, + /// stability fees, and oracle configurations. Use this for complete collateral onboarding. + /// @param co Struct containing all collateral configuration options and parameters function addNewCollateral(CollateralOpts memory co) public { // Add the collateral to the system. addCollateralBase(co.ilk, co.gem, co.join, co.clip, co.calc, co.pip); @@ -1064,6 +1130,7 @@ library DssExecLib { /// @param _start The start value for the target parameter /// @param _end The end value for the target parameter /// @param _duration The duration of the interpolation + /// @return The address of the created lerp contract function linearInterpolation( bytes32 _name, address _target, @@ -1088,6 +1155,7 @@ library DssExecLib { /// @param _start The start value for the target parameter /// @param _end The end value for the target parameter /// @param _duration The duration of the interpolation + /// @return The address of the created lerp contract function linearInterpolation( bytes32 _name, address _target, @@ -1098,8 +1166,8 @@ library DssExecLib { uint256 _end, uint256 _duration ) public returns (address) { - address lerp = - LerpFactoryLike(lerpFab()).newIlkLerp(_name, _target, _ilk, _what, _startTime, _start, _end, _duration); + address lerp = LerpFactoryLike(lerpFab()) + .newIlkLerp(_name, _target, _ilk, _what, _startTime, _start, _end, _duration); Authorizable(_target).rely(lerp); LerpLike(lerp).tick(); return lerp; @@ -1107,7 +1175,7 @@ library DssExecLib { /* ----- SubDAO/Star Spells ----- */ - /// @dev Execute a start spell through its star proxy. + /// @dev Execute a star spell through its star proxy. /// @param _starProxy The proxy to execute the spell through. /// @param _starSpell The spell to execute. /// @return The return data from the spell execution.