From 27c0d46d8a13e46c829f70c0a036c8aabe643577 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Sun, 6 Oct 2024 12:46:57 +0200 Subject: [PATCH 01/44] cleanup --- Makefile | 2 +- scripts/test-dssspell-forge.sh | 2 +- src/DssSpell.sol | 257 +---------- src/DssSpell.t.sol | 327 +------------- .../dss-direct-deposit/D3MCoreInstance.sol | 22 - .../dss-direct-deposit/D3MInit.sol | 420 ------------------ .../dss-direct-deposit/D3MInstance.sol | 23 - .../dss-lite-psm/DssLitePsmMigration.sol | 193 -------- .../phase-3/DssLitePsmMigrationPhase3.sol | 89 ---- src/test/config.sol | 10 +- 10 files changed, 12 insertions(+), 1333 deletions(-) delete mode 100644 src/dependencies/dss-direct-deposit/D3MCoreInstance.sol delete mode 100644 src/dependencies/dss-direct-deposit/D3MInit.sol delete mode 100644 src/dependencies/dss-direct-deposit/D3MInstance.sol delete mode 100644 src/dependencies/dss-lite-psm/DssLitePsmMigration.sol delete mode 100644 src/dependencies/dss-lite-psm/phase-3/DssLitePsmMigrationPhase3.sol diff --git a/Makefile b/Makefile index 7ad42f1f6..1e2ff795e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ all :; DAPP_LIBRARIES=' lib/dss-exec-lib/src/DssExecLib.sol:DssExecLib:$(shell cat DssExecLib.address)' \ - DAPP_BUILD_OPTIMIZE=1 DAPP_BUILD_OPTIMIZE_RUNS=200 \ + DAPP_BUILD_OPTIMIZE=0 DAPP_BUILD_OPTIMIZE_RUNS=200 \ DAPP_REMAPPINGS=$$(cat remappings.txt) \ dapp --use solc:0.8.16 build clean :; forge clean diff --git a/scripts/test-dssspell-forge.sh b/scripts/test-dssspell-forge.sh index 26d2f3df8..d4d82c483 100755 --- a/scripts/test-dssspell-forge.sh +++ b/scripts/test-dssspell-forge.sh @@ -19,7 +19,7 @@ done DSS_EXEC_LIB=$(< DssExecLib.address) echo "Using DssExecLib at: $DSS_EXEC_LIB" export FOUNDRY_LIBRARIES="lib/dss-exec-lib/src/DssExecLib.sol:DssExecLib:$DSS_EXEC_LIB" -export FOUNDRY_OPTIMIZER=true +export FOUNDRY_OPTIMIZER=false export FOUNDRY_OPTIMIZER_RUNS=200 export FOUNDRY_ROOT_CHAINID=1 diff --git a/src/DssSpell.sol b/src/DssSpell.sol index 3ed0acfa9..0b3a1605e 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -18,22 +18,13 @@ pragma solidity 0.8.16; import "dss-exec-lib/DssExec.sol"; import "dss-exec-lib/DssAction.sol"; -import { DssInstance, MCD } from "dss-test/MCD.sol"; -import { DssLitePsmMigrationPhase3, DssLitePsmMigrationConfigPhase3 } from "./dependencies/dss-lite-psm/phase-3/DssLitePsmMigrationPhase3.sol"; -import { D3MInit, D3MCommonConfig, D3MOperatorPlanConfig, D3MAaveUSDSPoolConfig } from "src/dependencies/dss-direct-deposit/D3MInit.sol"; -import { D3MInstance } from "src/dependencies/dss-direct-deposit/D3MInstance.sol"; - -interface SUsdsLike { - function file(bytes32, uint256) external; - function drip() external returns (uint256); -} 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/7018db4f449dc84fd250302b87734bd7aa66620c/governance/votes/Executive%20vote%20-%20October%2004%2C%202024.md' -q -O - 2>/dev/null)" + // Hash: cast keccak -- "$(wget 'TODO' -q -O - 2>/dev/null)" string public constant override description = - "2024-10-04 MakerDAO Executive Spell | Hash: 0xcc25bed2b24d0e3735fc7fadced432daf59890454cde7a4e65373b9fa5c55f26"; + "2024-10-17 MakerDAO Executive Spell | Hash: TODO"; // Set office hours according to the summary function officeHours() public pure override returns (bool) { @@ -51,250 +42,8 @@ contract DssSpellAction is DssAction { // https://ipfs.io/ipfs/QmVp4mhhbwWGTfbh2BzwQB9eiBrQBKiqcPRZCaAxNUaar6 // // uint256 internal constant X_PCT_RATE = ; - uint256 internal constant FIVE_PT_FIVE_PCT_RATE = 1000000001697766583380253701; - uint256 internal constant SIX_PT_FIVE_PCT_RATE = 1000000001996917783620820123; - uint256 internal constant NINE_PCT_RATE = 1000000002732676825177582095; - uint256 internal constant NINE_PT_TWO_FIVE_PCT_RATE = 1000000002805322428706865331; - uint256 internal constant NINE_PT_SEVEN_FIVE_PCT_RATE = 1000000002950116251408586949; - - // --- Math --- - uint256 internal constant MILLION = 10 ** 6; - uint256 internal constant BILLION = 10 ** 9; - uint256 internal constant WAD = 10 ** 18; - uint256 internal constant RAY = 10 ** 27; - uint256 internal constant RAD = 10 ** 45; - - // --- Offboarding: Current Liquidation Ratio --- - uint256 constant CURRENT_WBTC_A_MAT = 145 * RAY / 100; - uint256 constant CURRENT_WBTC_B_MAT = 130 * RAY / 100; - - // --- Offboarding: Target Liquidation Ratio --- - uint256 constant TARGET_WBTC_A_MAT = 150 * RAY / 100; - uint256 constant TARGET_WBTC_B_MAT = 150 * RAY / 100; - - // ---------- Contracts ---------- - address internal immutable D3M_HUB = DssExecLib.getChangelogAddress("DIRECT_HUB"); - address internal immutable D3M_MOM = DssExecLib.getChangelogAddress("DIRECT_MOM"); - address internal immutable MCD_PSM_USDC_A = DssExecLib.getChangelogAddress("MCD_PSM_USDC_A"); - address internal immutable SUSDS = DssExecLib.getChangelogAddress("SUSDS"); - address internal immutable USDS = DssExecLib.getChangelogAddress("USDS"); - address internal immutable USDS_JOIN = DssExecLib.getChangelogAddress("USDS_JOIN"); - address internal immutable MCD_PAUSE_PROXY = DssExecLib.getChangelogAddress("MCD_PAUSE_PROXY"); - address internal immutable MCD_SPOT = DssExecLib.getChangelogAddress("MCD_SPOT"); - - - address internal constant DIRECT_SPK_AAVE_LIDO_USDS_PLAN = 0xea2abB24bF40ac97746AFf6daCA0BBF885014b31; - address internal constant DIRECT_SPK_AAVE_LIDO_USDS_POOL = 0xbf674d0cD6841C1d7f9b8E809B967B3C5E867653; - address internal constant DIRECT_SPK_AAVE_LIDO_USDS_ORACLE = 0x9dB0EB29c2819f9AE0A91A6E6f644C35a7493E9b; - address internal constant DIRECT_SPK_AAVE_LIDO_USDS_OPERATOR = 0x298b375f24CeDb45e936D7e21d6Eb05e344adFb5; - address internal constant DIRECT_SPK_AAVE_LIDO_USDS_AUSDS = 0x09AA30b182488f769a9824F15E6Ce58591Da4781; - address internal constant DIRECT_SPK_AAVE_LIDO_USDS_STABLE_DEBT = 0x779dB175167C60c2B2193Be6B8d8B3602435e89E; - address internal constant DIRECT_SPK_AAVE_LIDO_USDS_VARIABLE_DEBT = 0x2D9fe18b6c35FE439cC15D932cc5C943bf2d901E; - - - function actions() public override { - // ---------- Stability Scope Parameter Changes ---------- - // Forum: https://forum.makerdao.com/t/stability-scope-parameter-changes-16-sfs-ssr-dsr-spark-effective-dai-borrow-rate-changes/25257 - // Forum: https://forum.sky.money/t/stability-scope-parameter-changes-16-sfs-ssr-dsr-spark-effective-dai-borrow-rate-changes/25257/2 - - // Stability Fee (SF) changes: - // Note: only heading, changes follow - - // WBTC-A: Increase by 1.5 percentage points, from 7.75% to 9.25% - DssExecLib.setIlkStabilityFee("WBTC-A", NINE_PT_TWO_FIVE_PCT_RATE, /* doDrip = */ true); - - // WBTC-B: Increase by 1.5 percentage points, from 8.25% to 9.75% - DssExecLib.setIlkStabilityFee("WBTC-B", NINE_PT_SEVEN_FIVE_PCT_RATE, /* doDrip = */ true); - - // WBTC-C: Increase by 1.5 percentage points, from 7.5% to 9% - DssExecLib.setIlkStabilityFee("WBTC-C", NINE_PCT_RATE, /* doDrip = */ true); - - // Dai & SKY Savings Rate: - // Note: only heading, changes follow - - // DSR: Decrease by 0.5 percentage points, from 6% to 5.5% - DssExecLib.setDSR(FIVE_PT_FIVE_PCT_RATE, /* doDrip = */ true); - - // SSR: Increase by 0.25 percentage points, from 6.25% to 6.5% - SUsdsLike(SUSDS).drip(); - SUsdsLike(SUSDS).file("ssr", SIX_PT_FIVE_PCT_RATE); - - // ---------- Update PSM-USDC-A Fees ---------- - // Forum: https://forum.makerdao.com/t/lite-psm-usdc-a-phase-3-final-migration-proposed-parameters/25183 - // Forum: https://forum.makerdao.com/t/lite-psm-usdc-a-phase-3-final-migration-proposed-parameters/25183/2 - // Poll: https://vote.makerdao.com/polling/QmRjrFYG - - // PSM-USDC-A tin: Decrease by 0.01 percentage points, from 0.01% to 0% - // Note: this is done via the DssLitePsmMigrationPhase3 script: line 78 - - // PSM-USDC-A tout: Decrease by 0.01 percentage points, from 0.01% to 0% - // Note: this is done via the DssLitePsmMigrationPhase3 script: line 79 - - // ---------- Phase 3 USDC Migration from PSM-USDC-A to LITE-PSM-USDC-A ---------- - // Forum: https://forum.makerdao.com/t/lite-psm-usdc-a-phase-3-final-migration-proposed-parameters/25183 - // Forum: https://forum.makerdao.com/t/lite-psm-usdc-a-phase-3-final-migration-proposed-parameters/25183/2 - // Poll: https://vote.makerdao.com/polling/QmRjrFYG - - // ---------- Update PSM-USDC-A DC-IAM ---------- - // Forum: https://forum.makerdao.com/t/lite-psm-usdc-a-phase-3-final-migration-proposed-parameters/25183 - // Forum: https://forum.makerdao.com/t/lite-psm-usdc-a-phase-3-final-migration-proposed-parameters/25183/2 - // Poll: https://vote.makerdao.com/polling/QmRjrFYG - - // ---------- Update MCD_LITE_PSM_USDC_A Buf ---------- - // Forum: https://forum.makerdao.com/t/lite-psm-usdc-a-phase-3-final-migration-proposed-parameters/25183 - // Forum: https://forum.makerdao.com/t/lite-psm-usdc-a-phase-3-final-migration-proposed-parameters/25183/2 - // Poll: https://vote.makerdao.com/polling/QmRjrFYG - - // ---------- Update LITE-PSM-USDC-A DC-IAM ---------- - // Forum: https://forum.makerdao.com/t/lite-psm-usdc-a-phase-3-final-migration-proposed-parameters/25183 - // Forum: https://forum.makerdao.com/t/lite-psm-usdc-a-phase-3-final-migration-proposed-parameters/25183/2 - // Poll: https://vote.makerdao.com/polling/QmRjrFYG - - // Migrate all remaining USDC reserves from PSM-USDC-A to LITE-PSM-USDC-A with a script executed in the spell - // Note: only heading, copied from the exec sheet for consistency - // PSM-USDC-A DC-IAM line: Decrease by 2,5 billion DAI, from 2,5 billion DAI to 0 - // Note: this is done via the DssLitePsmMigrationPhase3 script - - // Disable PSM-USDC-A DC-IAM - // Note: this is done via the DssLitePsmMigrationPhase3 script - - // Note: load the MCD contracts depencencies - DssInstance memory dss = MCD.loadFromChainlog(DssExecLib.LOG); - - // Note: specify the migration config - DssLitePsmMigrationConfigPhase3 memory migrationCfg = DssLitePsmMigrationConfigPhase3({ - // Note: chainlog key of LITE-PSM-USDC-A - dstPsmKey: "MCD_LITE_PSM_USDC_A", - - // MCD_LITE_PSM_USDC_A buf: Increase by 200 million DAI, from 200 million DAI to 400 million DAI - dstBuf: 400 * MILLION * WAD, - - // LITE-PSM-USDC-A DC-IAM line: Increase by 2,5 billion DAI, from 7,5 billion DAI to 10 billion DAI. - dstMaxLine: 10 * BILLION * RAD, - - // LITE-PSM-USDC-A DC-IAM gap: Increase by 200 million DAI, from 200 million DAI to 400 million DAI. - dstGap: 400 * MILLION * RAD, - - // LITE-PSM-USDC-A DC-IAM ttl: 12h (Unchanged) - dstTtl: 12 hours, - - // Note: chainlog key of PSM-USDC-A - srcPsmKey: "MCD_PSM_USDC_A" - - }); - - // Note: LitePSM migration was extracted into a library, - // and implemented as part of the LitePSM module. - DssLitePsmMigrationPhase3.migrate(dss, migrationCfg); - - // ---------- Activate Aave Lido Market USDS DDM ---------- - // Forum: https://forum.makerdao.com/t/risk-assessment-and-parameter-recommendations-spark-ddm-to-aave-lido-market/25175 - // Forum: https://forum.makerdao.com/t/risk-assessment-and-parameter-recommendations-spark-ddm-to-aave-lido-market/25175/2 - - // Add new ilk as DIRECT-SPK-AAVE-LIDO-USDS - // Set DDM DC-IAM with the following parameters: - // line: 100 million USDS - // gap: 50 million USDS - // ttl: 24 hours - // tau: 7 days - // DDM Addresses: - // oracle: D3MOracle at 0x9dB0EB29c2819f9AE0A91A6E6f644C35a7493E9b - // plan: D3MOperatorPlan at 0xea2abB24bF40ac97746AFf6daCA0BBF885014b31 - // pool: D3MAaveV3USDSNoSupplyCapTypePool at 0xbf674d0cD6841C1d7f9b8E809B967B3C5E867653 - // aToken: 0x09AA30b182488f769a9824F15E6Ce58591Da4781 - // operator: 0x298b375f24CeDb45e936D7e21d6Eb05e344adFb5 - // stabledebt address: 0x779dB175167C60c2B2193Be6B8d8B3602435e89E - // variabledebt address: 0x2D9fe18b6c35FE439cC15D932cc5C943bf2d901E - // Additional Actions - // Expand DIRECT_MOM breaker to also include new DDM - // Note: this is already done within D3MInit.sol line 232 - - D3MInstance memory d3m = D3MInstance({ - plan: DIRECT_SPK_AAVE_LIDO_USDS_PLAN, - pool: DIRECT_SPK_AAVE_LIDO_USDS_POOL, - oracle: DIRECT_SPK_AAVE_LIDO_USDS_ORACLE - }); - - D3MCommonConfig memory d3mCfg = D3MCommonConfig({ - hub: D3M_HUB, - mom: D3M_MOM, - ilk: "DIRECT-SPK-AAVE-LIDO-USDS", - existingIlk: false, - maxLine: 100 * MILLION * RAD, - gap: 50 * MILLION * RAD, - ttl: 24 hours, - tau: 7 days - }); - - D3MAaveUSDSPoolConfig memory aaveCfg = D3MAaveUSDSPoolConfig({ - king: MCD_PAUSE_PROXY, - ausds: DIRECT_SPK_AAVE_LIDO_USDS_AUSDS, - usdsJoin: USDS_JOIN, - usds: USDS, - stableDebt: DIRECT_SPK_AAVE_LIDO_USDS_STABLE_DEBT, - variableDebt: DIRECT_SPK_AAVE_LIDO_USDS_VARIABLE_DEBT - }); - - D3MOperatorPlanConfig memory operatorCfg = D3MOperatorPlanConfig({ - operator: DIRECT_SPK_AAVE_LIDO_USDS_OPERATOR - }); - - D3MInit.initCommon({ - dss: dss, - d3m: d3m, - cfg: d3mCfg - }); - - D3MInit.initAaveUSDSPool({ - dss: dss, - d3m: d3m, - cfg: d3mCfg, - aaveCfg: aaveCfg - }); - - D3MInit.initOperatorPlan({ - d3m: d3m, - operatorCfg: operatorCfg - }); - - // ---------- Update WBTC Legacy Vaults Parameters ---------- - // Forum: https://forum.makerdao.com/t/wbtc-changes-and-risk-mitigation-10-august-2024/24844/48 - // Forum: https://forum.sky.money/t/wbtc-changes-and-risk-mitigation-10-august-2024/24844/52 - - // Decrease liquidation penalty for WBTC-A, WBTC-B, and WBTC-C from 13% to 0% - DssExecLib.setIlkLiquidationPenalty("WBTC-A", 0); - DssExecLib.setIlkLiquidationPenalty("WBTC-B", 0); - DssExecLib.setIlkLiquidationPenalty("WBTC-C", 0); - - // WBTC-A: Increase LERP for liquidation ratios from 145% to 150% over 6 days - DssExecLib.linearInterpolation({ - _name: "WBTC-A Parameter Update", - _target: MCD_SPOT, - _ilk: "WBTC-A", - _what: "mat", - _startTime: block.timestamp, - _start: CURRENT_WBTC_A_MAT, - _end: TARGET_WBTC_A_MAT, - _duration: 6 days - }); - - // WBTC-B: Increase LERP for liquidation ratios from 130% to 150% over 6 days - DssExecLib.linearInterpolation({ - _name: "WBTC-B Parameter Update", - _target: MCD_SPOT, - _ilk: "WBTC-B", - _what: "mat", - _startTime: block.timestamp, - _start: CURRENT_WBTC_B_MAT, - _end: TARGET_WBTC_B_MAT, - _duration: 6 days - }); - - // ---------- Chainlog bump ---------- - // Note: we need to increase chainlog version as D3MInit.initCommon added new keys - DssExecLib.setChangelogVersion("1.19.1"); - } + function actions() public override {} } contract DssSpell is DssExec { diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index 9dca41ecc..4bb75cb03 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -49,51 +49,6 @@ interface SequencerLike { function hasJob(address job) external view returns (bool); } -interface D3MHubLike { - function end() external view returns (address); - function exec(bytes32 ilk) external; - function ilks(bytes32) - external - view - returns (address pool, address plan, uint256 tau, uint256 culled, uint256 tic); - function vow() external view returns (address); -} - -interface D3MMomLike { - function authority() external view returns (address); - function disable(address who) external; -} - -interface D3MAaveUSDSPoolLike { - function hub() external view returns (address); - function dai() external view returns (address); - function ilk() external view returns (bytes32); - function vat() external view returns (address); - function file(bytes32, address) external; - function ausds() external view returns (address); - function usdsJoin() external view returns (address); - function usds() external view returns (address); - function daiJoin() external view returns (address); - function stableDebt() external view returns (address); - function variableDebt() external view returns (address); - function redeemable() external view returns (address); - function king() external view returns (address); -} - -interface D3MOracleLike { - function hub() external view returns (address); - function vat() external view returns (address); - function ilk() external view returns (bytes32); -} - -interface D3MOperatorPlanLike { - function active() external view returns (bool); - function getTargetAssets(uint256) external view returns (uint256); - function setTargetAssets(uint256 value) external; - function operator() external view returns (address); - function wards(address) external view returns (uint256); -} - contract DssSpellTest is DssSpellTestBase { string config; RootDomain rootDomain; @@ -157,7 +112,7 @@ contract DssSpellTest is DssSpellTestBase { _testSystemTokens(); } - // Leave this test public (for now) as this is acting like a config test + // Leave this test always enabled as it acts as a config test function testPSMs() public { _vote(address(spell)); _scheduleWaitAndCast(address(spell)); @@ -214,6 +169,7 @@ contract DssSpellTest is DssSpellTestBase { ); } + // Leave this test always enabled as it acts as a config test function testLitePSMs() public { _vote(address(spell)); _scheduleWaitAndCast(address(spell)); @@ -955,283 +911,4 @@ contract DssSpellTest is DssSpellTestBase { } // SPELL-SPECIFIC TESTS GO BELOW - bytes32 constant SRC_ILK = "PSM-USDC-A"; - bytes32 constant DST_ILK = "LITE-PSM-USDC-A"; - PsmAbstract immutable srcPsm = PsmAbstract( addr.addr("MCD_PSM_USDC_A")); - LitePsmLike immutable dstPsm = LitePsmLike( addr.addr("MCD_LITE_PSM_USDC_A")); - GemAbstract immutable gem = GemAbstract( addr.addr("USDC")); - address immutable pocket = addr.addr("MCD_LITE_PSM_USDC_A_POCKET"); - uint256 constant srcKeep = 0; - uint256 constant dstWant = type(uint256).max; - uint256 constant dstBuf = 400 * MILLION * WAD; - uint256 constant dstMaxLine = 10 * BILLION * RAD; - uint256 constant dstGap = 400 * MILLION * RAD; - uint256 constant dstTtl = 12 hours; - uint256 constant srcTin = 0; - uint256 constant srcTout = 0; - - // this is declared here because it was the only way to avoid "stack too deep" errors - uint256 pDaiSum; - - function test_LITE_PSM_USDC_A_MigrationPhase3() public { - (uint256 psrcInk, uint256 psrcArt) = vat.urns(SRC_ILK, address(srcPsm)); - uint256 psrcVatGem = vat.gem(SRC_ILK, address(srcPsm)); - uint256 psrcGemBalance = gem.balanceOf(address(srcPsm.gemJoin())); - (uint256 pdstInk, uint256 pdstArt) = vat.urns(DST_ILK, address(dstPsm)); - uint256 pdstVatGem = vat.gem(DST_ILK, address(dstPsm)); - uint256 pdstGemBalance = gem.balanceOf(address(pocket)); - uint256 pvice = vat.vice(); - uint256 ppauseSin = vat.sin(pauseProxy); - - uint256 expectedMoveWad = _min(dstWant, _subcap(psrcInk, srcKeep)); - - // This is a hack: drip() is called twice in the spell and as a consequence vice is incresed; - // In order to prove that the migration did not create any extra debt we do it implicitly by - // calculating the dai/usds balances of dsr and susds before and after which gives us the actual increase - pDaiSum = vat.dai(address(pot)) + (usds.balanceOf(address(susds)) * RAY); - - // ----- Pre-spell sanity checks ----- - { - (uint256 psrcIlkArt,,, uint256 psrcLine,) = vat.ilks(SRC_ILK); - - assertGt(psrcIlkArt, 0, "before: src ilk Art is zero"); - assertGt(psrcLine, 0, "before: src line is zero"); - assertGt(psrcArt, 0, "before: src art is zero"); - assertGt(psrcInk, 0, "before: src ink is zero"); - assertGt(srcPsm.tin(), 0, "before: src tin is zero"); - assertGt(srcPsm.tout(), 0, "before: src tout is zero"); - assertEq(dstPsm.buf(), 200 * MILLION * WAD, "before: dst buf does not match"); - } - - { - (uint256 maxLine, uint256 gap, uint48 ttl,,) = autoLine.ilks(DST_ILK); - assertEq(maxLine, 7_500 * MILLION * RAD, "before: dst maxLine does not match"); - assertEq(gap, 200 * MILLION * RAD, "before: dst gap does not match"); - assertEq(ttl, 12 hours, "before: dst ttl does not match"); - } - - // ----- Execute spell ----- - _vote(address(spell)); - _scheduleWaitAndCast(address(spell)); - assertTrue(spell.done()); - - // ----- Post-spell state checks ----- - - // Sanity checks - assertEq(srcPsm.tin(), srcTin, "after: invalid src tin"); - assertEq(srcPsm.tout(), srcTout, "after: invalid src tout"); - assertEq(srcPsm.vow(), address(vow), "after: unexpected src vow update"); - - assertEq(dstPsm.buf(), dstBuf, "after: invalid dst buf"); - assertEq(dstPsm.vow(), address(vow), "after: unexpected dst vow update"); - - // No bad debt is left behind - { - // The incraase in vice should be solely caused by spell actions not related to the migration - uint256 viceIncrease = vat.dai(address(pot)) + (usds.balanceOf(address(susds)) * RAY) - pDaiSum; - assertEq(vat.vice(), pvice + viceIncrease, "after: vice mismatch"); - assertEq(vat.sin(pauseProxy), ppauseSin, "after: sin mismatch"); - } - - // Old PSM state is set correctly - { - (uint256 srcInk, uint256 srcArt) = vat.urns(SRC_ILK, address(srcPsm)); - assertEq(srcInk, psrcInk - expectedMoveWad, "after: src ink is not decreased by the moved amount"); - assertEq(srcInk, srcKeep, "after: src ink is different from src keep"); - assertEq(srcArt, psrcArt - expectedMoveWad, "after: src art is not decreased by the moved amount"); - assertEq(vat.gem(SRC_ILK, address(srcPsm)), psrcVatGem, "after: unexpected src vat gem change"); - assertEq( - _amtToWad(gem.balanceOf(address(srcPsm.gemJoin()))), - _amtToWad(psrcGemBalance) - expectedMoveWad, - "after: invalid gem balance for src gemJoin" - ); - } - // Old PSM line is set to 0 - { - (,,, uint256 psrcLine,) = vat.ilks(SRC_ILK); - assertEq(psrcLine, 0, "after: AutoLine invalid maxLine"); - } - // Old PSM is removed from the AutoLine - { - (uint256 maxLine, uint256 gap, uint48 ttl, uint256 last,) = autoLine.ilks(SRC_ILK); - assertEq(maxLine, 0, "after: AutoLine invalid maxLine"); - assertEq(gap, 0, "after: AutoLine invalid gap"); - assertEq(ttl, 0, "after: AutoLine invalid ttl"); - assertEq(last, 0, "after: AutoLine invalid last"); - } - - // New PSM state is set correctly - { - // LitePSM ink is never modified - (uint256 dstInk, uint256 dstArt) = vat.urns(DST_ILK, address(dstPsm)); - assertEq(dstInk, pdstInk, "after: unexpected dst ink chagne"); - // There might be extra `art` because of the calls to `fill`. - assertGe(dstArt, pdstArt + expectedMoveWad, "after: dst art is not increased at least by the moved amount"); - // if someone has sent dai to the psm contract then cut() will return a non-zero value - // so we have to account for this and subtract it from the reported balance. - uint256 excessDai = dstPsm.cut(); - assertEq(dai.balanceOf(address(dstPsm)) - excessDai, dstBuf, "after: invalid dst psm dai balance"); - assertEq(vat.gem(DST_ILK, address(dstPsm)), pdstVatGem, "after: unexpected dst vat gem change"); - assertEq( - _amtToWad(gem.balanceOf(address(pocket))), - _amtToWad(pdstGemBalance) + expectedMoveWad, - "after: invalid gem balance for dst pocket" - ); - } - - // New PSM is properly configured on AutoLine - { - (uint256 maxLine, uint256 gap, uint48 ttl, uint256 last, uint256 lastInc) = autoLine.ilks(DST_ILK); - assertEq(maxLine, dstMaxLine, "after: AutoLine invalid maxLine"); - assertEq(gap, dstGap, "after: AutoLine invalid gap"); - assertEq(ttl, uint48(dstTtl), "after: AutoLine invalid ttl"); - assertEq(last, block.number, "after: AutoLine invalid last"); - assertEq(lastInc, block.timestamp, "after: AutoLine invalid lastInc"); - } - } - - function _amtToWad(uint256 amt) internal view returns (uint256) { - return amt * dstPsm.to18ConversionFactor(); - } - - function _min(uint256 x, uint256 y) internal pure returns (uint256 z) { - z = x < y ? x : y; - } - - function _subcap(uint256 x, uint256 y) internal pure returns (uint256 z) { - z = x < y ? 0 : x - y; - } - - function testDirectSparAaveLidoUSDSIntegration() public { - bytes32 ilk = "DIRECT-SPK-AAVE-LIDO-USDS"; - address operator = 0x298b375f24CeDb45e936D7e21d6Eb05e344adFb5; - - _vote(address(spell)); - _scheduleWaitAndCast(address(spell)); - assertTrue(spell.done()); - - D3MHubLike hub = D3MHubLike(addr.addr("DIRECT_HUB")); - D3MMomLike mom = D3MMomLike(addr.addr("DIRECT_MOM")); - D3MAaveUSDSPoolLike pool = D3MAaveUSDSPoolLike(addr.addr("DIRECT_SPK_AAVE_LIDO_USDS_POOL")); - D3MOperatorPlanLike plan = D3MOperatorPlanLike(addr.addr("DIRECT_SPK_AAVE_LIDO_USDS_PLAN")); - D3MOracleLike oracle = D3MOracleLike(addr.addr("DIRECT_SPK_AAVE_LIDO_USDS_ORACLE")); - - // ----- Post-spell state checks ----- - - // New ilk is properly configured on AutoLine - { - uint256 maxLine = 100 * MILLION * RAD; - uint256 gap = 50 * MILLION * RAD; - uint256 ttl = 24 hours; - (uint256 _maxLine, uint256 _gap, uint48 _ttl, uint256 _last,) = autoLine.ilks(ilk); - assertEq(_maxLine, maxLine, "D3M: AutoLine invalid maxLine"); - assertEq(_gap, gap, "D3M: AutoLine invalid gap"); - assertEq(_ttl, uint48(ttl), "D3M: AutoLine invalid ttl"); - assertEq(_last, 0, "D3M: AutoLine invalid last"); - } - - // Test D3MHub - { - (address _pool, address _plan, uint256 tau, uint256 _culled,) = hub.ilks(ilk); - assertEq(_pool, address(pool), "hub: unexpected pool address"); - assertEq(_plan, address(plan), "hub: unexpected plan address"); - assertEq(tau, 7 days, "hub: unexpected tau value"); - assertEq(_culled, 0, "hub: culled not 0"); - assertEq(hub.vow(), address(vow), "hub: unexpected vow address"); - assertEq(hub.end(), address(end), "hub: unexpected vow address"); - assertEq(vat.wards(address(hub)), 1, "hub: not relied"); - // Adjust debt - uint256 debtCeiling = 1 * MILLION * WAD; - vm.prank(operator); - plan.setTargetAssets(debtCeiling); - assertEq(plan.getTargetAssets(0), debtCeiling, 'TestError/targetAssets-not-set'); - - // Fill by the line - hub.exec(ilk); - (uint256 ink, uint256 art) = vat.urns(ilk, address(pool)); - assertEq(ink, debtCeiling, "TestError/unexpected-postexec-ink"); - assertEq(art, debtCeiling, "TestError/unexpected-postexec-art"); - } - - // Test MCD_SPOT - { - (address pip, uint256 mat) = spotter.ilks(ilk); - assertEq(pip, address(oracle), "spot:unexpected pip address"); - assertEq(mat, RAY, "spot:unexpected mat value"); - } - - // D3MPool checks - { - // Set here to avoid stack too deep errors! - address stableDebt = 0x779dB175167C60c2B2193Be6B8d8B3602435e89E; - address variableDebt = 0x2D9fe18b6c35FE439cC15D932cc5C943bf2d901E; - address ausds = 0x09AA30b182488f769a9824F15E6Ce58591Da4781; - assertEq(pool.hub(), address(hub), "pool: unexpected hub address"); - assertEq(pool.ilk(), ilk, "pool: unexpected ilk"); - assertEq(pool.vat(), address(vat), "pool: unexpected vat address"); - assertEq(pool.dai(), address(dai), "pool: unexpected dai address"); - assertEq(pool.ausds(), address(ausds), "pool: unexpected ausds address"); - assertEq(pool.usds(), address(usds), "pool: unexpected usds address"); - assertEq(pool.usdsJoin(), address(usdsJoin), "pool: unexpected usdsJoin address"); - assertEq(pool.daiJoin(), address(daiJoin), "pool: unexpected daiJoin address"); - assertEq(pool.stableDebt(), stableDebt, "pool: unexpected stableDebt address"); - assertEq(pool.variableDebt(), variableDebt, "pool: unexpected variableDebt address"); - assertEq(pool.redeemable(), address(ausds), "pool: redeemable does not match"); - assertEq(pool.king(), address(pauseProxy), "pool: king does not match pause proxy"); - } - - // D3MOracle checks - { - assertEq(oracle.ilk(), ilk, "oracle: unexpected ilk"); - assertEq(oracle.hub(), address(hub), "oracle: unexpected hub address"); - assertEq(oracle.vat(), address(vat), "oracle: unexpected vat address"); - assertEq(oracle.hub(), address(hub), "oracle: unexpected vat address"); - } - - // D3MPlan checks - { - assertEq(plan.operator(), operator, "plan: unexpected operator address"); - assertEq(plan.wards(address(mom)), 1, "plan: mom not relied"); - assertEq(plan.active(), true, "plan: inactive"); - // Ensure targetAssets can be updated by the operator - vm.prank(operator); - plan.setTargetAssets(100e18); - assertEq(plan.getTargetAssets(0), 100e18, "plan: unexpected target assets value"); - } - - // Test D3MMom - { - assertEq(mom.authority(), address(chief), "mom: unexpected authority address"); - // Deactivate the D3M via mom - vm.prank(DSChiefAbstract(chief).hat()); - mom.disable(address(plan)); - assertEq(plan.active(), false, "mom: still active"); - - hub.exec(ilk); - (uint256 ink, uint256 art) = vat.urns(ilk, address(pool)); - assertLt(ink, WAD, "mom: unexpected ink amount"); // Less than some dust amount is fine (1 DAI) - assertLt(art, WAD, "mom: unexpected art amount"); - } - - // Test new ILK_REGISTRY - { - assertEq(reg.pos(ilk), 66, "ilk reg: unexpected postion"); - assertEq(reg.join(ilk), addr.addr("DIRECT_HUB"), "ilk reg: unexpected join address"); - assertEq(reg.gem(ilk), 0x09AA30b182488f769a9824F15E6Ce58591Da4781, "ilk reg: unexpected gem address"); // ausds - assertEq(reg.dec(ilk), 18, "ilk reg: decimals"); // ausds has 18 decimals - assertEq(reg.class(ilk), 4, "ilk reg: unexpected class"); - assertEq(reg.pip(ilk), address(oracle), "ilk reg: unexpected pip address"); - assertEq(reg.xlip(ilk), address(0), "ilk reg: unexpected xlip address"); - assertEq(reg.name(ilk), "Aave Ethereum Lido USDS", "ilk reg: unexpected name"); // ausds name - assertEq(reg.symbol(ilk), "aEthLidoUSDS", "ilk reg: unexpected symbol"); // ausds symbol - } - } - - function test_WBTC_A_LerpOffboarding() public { - _checkIlkLerpOffboarding("WBTC-A", "WBTC-A Parameter Update", 145, 150); - } - - function test_WBTC_B_LerpOffboarding() public { - _checkIlkLerpOffboarding("WBTC-B", "WBTC-B Parameter Update", 130, 150); - } } diff --git a/src/dependencies/dss-direct-deposit/D3MCoreInstance.sol b/src/dependencies/dss-direct-deposit/D3MCoreInstance.sol deleted file mode 100644 index a9a4a9ae2..000000000 --- a/src/dependencies/dss-direct-deposit/D3MCoreInstance.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: © 2022 Dai Foundation -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -pragma solidity >=0.8.0; - -struct D3MCoreInstance { - address hub; - address mom; -} diff --git a/src/dependencies/dss-direct-deposit/D3MInit.sol b/src/dependencies/dss-direct-deposit/D3MInit.sol deleted file mode 100644 index fe86ceebc..000000000 --- a/src/dependencies/dss-direct-deposit/D3MInit.sol +++ /dev/null @@ -1,420 +0,0 @@ -// SPDX-FileCopyrightText: © 2022 Dai Foundation -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -pragma solidity >=0.8.0; - -import "dss-interfaces/dss/DssAutoLineAbstract.sol"; -import "dss-interfaces/dss/IlkRegistryAbstract.sol"; -import "dss-interfaces/utils/WardsAbstract.sol"; -import "dss-interfaces/ERC/GemAbstract.sol"; -import { DssInstance } from "dss-test/MCD.sol"; -import { ScriptTools } from "dss-test/ScriptTools.sol"; - -import { D3MInstance } from "./D3MInstance.sol"; -import { D3MCoreInstance } from "./D3MCoreInstance.sol"; - -interface D3MAavePoolLike { - function hub() external view returns (address); - function dai() external view returns (address); - function ilk() external view returns (bytes32); - function vat() external view returns (address); - function file(bytes32, address) external; - function adai() external view returns (address); - function stableDebt() external view returns (address); - function variableDebt() external view returns (address); -} - -interface D3MAaveUSDSPoolLike { - function hub() external view returns (address); - function dai() external view returns (address); - function ilk() external view returns (bytes32); - function vat() external view returns (address); - function file(bytes32, address) external; - function ausds() external view returns (address); - function usdsJoin() external view returns (address); - function usds() external view returns (address); - function daiJoin() external view returns (address); - function stableDebt() external view returns (address); - function variableDebt() external view returns (address); -} - -interface D3MAaveRateTargetPlanLike { - function rely(address) external; - function file(bytes32, uint256) external; - function adai() external view returns (address); - function stableDebt() external view returns (address); - function variableDebt() external view returns (address); - function tack() external view returns (address); - function adaiRevision() external view returns (uint256); -} - -interface D3MAaveBufferPlanLike { - function file(bytes32, uint256) external; - function adai() external view returns (address); -} - -interface ADaiLike { - function ATOKEN_REVISION() external view returns (uint256); -} - -interface D3MCompoundPoolLike { - function hub() external view returns (address); - function dai() external view returns (address); - function ilk() external view returns (bytes32); - function vat() external view returns (address); - function file(bytes32, address) external; - function cDai() external view returns (address); - function comptroller() external view returns (address); - function comp() external view returns (address); -} - -interface D3MCompoundRateTargetPlanLike { - function rely(address) external; - function file(bytes32, uint256) external; - function tacks(address) external view returns (uint256); - function delegates(address) external view returns (uint256); - function cDai() external view returns (address); -} - -interface CDaiLike { - function interestRateModel() external view returns (address); - function implementation() external view returns (address); -} - -interface D3M4626PoolLike { - function hub() external view returns (address); - function dai() external view returns (address); - function ilk() external view returns (bytes32); - function vat() external view returns (address); - function vault() external view returns (address); -} - -interface D3MOperatorPlanLike { - function file(bytes32, address) external; -} - -interface D3MOracleLike { - function vat() external view returns (address); - function ilk() external view returns (bytes32); - function file(bytes32, address) external; -} - -interface D3MHubLike { - function vat() external view returns (address); - function daiJoin() external view returns (address); - function file(bytes32, address) external; - function file(bytes32, bytes32, address) external; - function file(bytes32, bytes32, uint256) external; -} - -interface D3MMomLike { - function setAuthority(address) external; -} - -interface RedeemableLike { - function redeemable() external view returns (address); -} - -struct D3MCommonConfig { - address hub; - address mom; - bytes32 ilk; - bool existingIlk; - uint256 maxLine; - uint256 gap; - uint256 ttl; - uint256 tau; -} - -struct D3MAavePoolConfig { - address king; - address adai; - address stableDebt; - address variableDebt; -} - -struct D3MAaveUSDSPoolConfig { - address king; - address ausds; - address usdsJoin; - address usds; - address stableDebt; - address variableDebt; -} - -struct D3MAaveRateTargetPlanConfig { - uint256 bar; - address adai; - address stableDebt; - address variableDebt; - address tack; - uint256 adaiRevision; -} - -struct D3MAaveBufferPlanConfig { - uint256 buffer; - address adai; -} - -struct D3MCompoundPoolConfig { - address king; - address cdai; - address comptroller; - address comp; -} - -struct D3MCompoundRateTargetPlanConfig { - uint256 barb; - address cdai; - address tack; - address delegate; -} - -struct D3M4626PoolConfig { - address vault; -} - -struct D3MOperatorPlanConfig { - address operator; -} - -// Init a D3M instance -library D3MInit { - - function initCore( - DssInstance memory dss, - D3MCoreInstance memory d3mCore - ) internal { - D3MHubLike hub = D3MHubLike(d3mCore.hub); - D3MMomLike mom = D3MMomLike(d3mCore.mom); - - // Sanity checks - require(hub.vat() == address(dss.vat), "Hub vat mismatch"); - require(hub.daiJoin() == address(dss.daiJoin), "Hub daiJoin mismatch"); - - hub.file("vow", address(dss.vow)); - hub.file("end", address(dss.end)); - - mom.setAuthority(dss.chainlog.getAddress("MCD_ADM")); - - dss.vat.rely(address(hub)); - - dss.chainlog.setAddress("DIRECT_HUB", address(hub)); - dss.chainlog.setAddress("DIRECT_MOM", address(mom)); - } - - function initCommon( - DssInstance memory dss, - D3MInstance memory d3m, - D3MCommonConfig memory cfg - ) internal { - bytes32 ilk = cfg.ilk; - D3MHubLike hub = D3MHubLike(cfg.hub); - D3MOracleLike oracle = D3MOracleLike(d3m.oracle); - - // Sanity checks - require(oracle.vat() == address(dss.vat), "Oracle vat mismatch"); - require(oracle.ilk() == ilk, "Oracle ilk mismatch"); - - WardsAbstract(d3m.plan).rely(cfg.mom); - - hub.file(ilk, "pool", d3m.pool); - hub.file(ilk, "plan", d3m.plan); - hub.file(ilk, "tau", cfg.tau); - - oracle.file("hub", address(hub)); - - dss.spotter.file(ilk, "pip", address(oracle)); - dss.spotter.file(ilk, "mat", 10 ** 27); // 100% as a RAY - uint256 previousIlkLine; - if (cfg.existingIlk) { - (,,, previousIlkLine,) = dss.vat.ilks(ilk); - } else { - dss.vat.init(ilk); - dss.jug.init(ilk); - } - dss.vat.file(ilk, "line", cfg.gap); - dss.vat.file("Line", dss.vat.Line() + cfg.gap - previousIlkLine); - DssAutoLineAbstract(dss.chainlog.getAddress("MCD_IAM_AUTO_LINE")).setIlk( - ilk, - cfg.maxLine, - cfg.gap, - cfg.ttl - ); - dss.spotter.poke(ilk); - - GemAbstract gem = GemAbstract(RedeemableLike(d3m.pool).redeemable()); - IlkRegistryAbstract(dss.chainlog.getAddress("ILK_REGISTRY")).put( - ilk, - address(hub), - address(gem), - gem.decimals(), - 4, // ilk registry class for D3Ms - address(oracle), - address(0), - gem.name(), - gem.symbol() - ); - - string memory clPrefix = ScriptTools.ilkToChainlogFormat(ilk); - dss.chainlog.setAddress(ScriptTools.stringToBytes32(string(abi.encodePacked(clPrefix, "_POOL"))), d3m.pool); - dss.chainlog.setAddress(ScriptTools.stringToBytes32(string(abi.encodePacked(clPrefix, "_PLAN"))), d3m.plan); - dss.chainlog.setAddress(ScriptTools.stringToBytes32(string(abi.encodePacked(clPrefix, "_ORACLE"))), d3m.oracle); - } - - /** - * @dev This works for V2 and V3 pool adapters. - */ - function initAavePool( - DssInstance memory dss, - D3MInstance memory d3m, - D3MCommonConfig memory cfg, - D3MAavePoolConfig memory aaveCfg - ) internal { - D3MAavePoolLike pool = D3MAavePoolLike(d3m.pool); - - // Sanity checks - require(pool.hub() == cfg.hub, "Pool hub mismatch"); - require(pool.ilk() == cfg.ilk, "Pool ilk mismatch"); - require(pool.vat() == address(dss.vat), "Pool vat mismatch"); - require(pool.dai() == address(dss.dai), "Pool dai mismatch"); - require(pool.adai() == aaveCfg.adai, "Pool adai mismatch"); - require(pool.stableDebt() == aaveCfg.stableDebt, "Pool stableDebt mismatch"); - require(pool.variableDebt() == aaveCfg.variableDebt, "Pool variableDebt mismatch"); - - pool.file("king", aaveCfg.king); - } - - function initAaveUSDSPool( - DssInstance memory dss, - D3MInstance memory d3m, - D3MCommonConfig memory cfg, - D3MAaveUSDSPoolConfig memory aaveCfg - ) internal { - D3MAaveUSDSPoolLike pool = D3MAaveUSDSPoolLike(d3m.pool); - - // Sanity checks - require(pool.hub() == cfg.hub, "Pool hub mismatch"); - require(pool.ilk() == cfg.ilk, "Pool ilk mismatch"); - require(pool.vat() == address(dss.vat), "Pool vat mismatch"); - require(pool.usdsJoin() == aaveCfg.usdsJoin, "Pool usdsJoin mismatch"); - require(pool.usds() == aaveCfg.usds, "Pool usds mismatch"); - require(pool.daiJoin() == address(dss.daiJoin), "Pool daiJoin mismatch"); - require(pool.dai() == address(dss.dai), "Pool dai mismatch"); - require(pool.ausds() == aaveCfg.ausds, "Pool ausds mismatch"); - require(pool.stableDebt() == aaveCfg.stableDebt, "Pool stableDebt mismatch"); - require(pool.variableDebt() == aaveCfg.variableDebt, "Pool variableDebt mismatch"); - - pool.file("king", aaveCfg.king); - } - - function initCompoundPool( - DssInstance memory dss, - D3MInstance memory d3m, - D3MCommonConfig memory cfg, - D3MCompoundPoolConfig memory compoundCfg - ) internal { - D3MCompoundPoolLike pool = D3MCompoundPoolLike(d3m.pool); - - // Sanity checks - require(pool.hub() == cfg.hub, "Pool hub mismatch"); - require(pool.ilk() == cfg.ilk, "Pool ilk mismatch"); - require(pool.vat() == address(dss.vat), "Pool vat mismatch"); - require(pool.dai() == address(dss.dai), "Pool dai mismatch"); - require(pool.comptroller() == compoundCfg.comptroller, "Pool comptroller mismatch"); - require(pool.comp() == compoundCfg.comp, "Pool comp mismatch"); - require(pool.cDai() == compoundCfg.cdai, "Pool cDai mismatch"); - - pool.file("king", compoundCfg.king); - } - - /** - * @dev Initialize a 4626 pool. - */ - function init4626Pool( - DssInstance memory dss, - D3MInstance memory d3m, - D3MCommonConfig memory cfg, - D3M4626PoolConfig memory erc4626Cfg - ) internal view { - D3M4626PoolLike pool = D3M4626PoolLike(d3m.pool); - - // Sanity checks - require(pool.hub() == cfg.hub, "Pool hub mismatch"); - require(pool.ilk() == cfg.ilk, "Pool ilk mismatch"); - require(pool.vat() == address(dss.vat), "Pool vat mismatch"); - require(pool.dai() == address(dss.dai), "Pool dai mismatch"); - require(pool.vault() == erc4626Cfg.vault, "Pool vault mismatch"); - } - - function initAaveRateTargetPlan( - D3MInstance memory d3m, - D3MAaveRateTargetPlanConfig memory aaveCfg - ) internal { - D3MAaveRateTargetPlanLike plan = D3MAaveRateTargetPlanLike(d3m.plan); - ADaiLike adai = ADaiLike(aaveCfg.adai); - - // Sanity checks - require(plan.adai() == address(adai), "Plan adai mismatch"); - require(plan.stableDebt() == aaveCfg.stableDebt, "Plan stableDebt mismatch"); - require(plan.variableDebt() == aaveCfg.variableDebt, "Plan variableDebt mismatch"); - require(plan.tack() == aaveCfg.tack, "Plan tack mismatch"); - require(plan.adaiRevision() == aaveCfg.adaiRevision, "Plan adaiRevision mismatch"); - require(adai.ATOKEN_REVISION() == aaveCfg.adaiRevision, "ADai adaiRevision mismatch"); - - plan.file("bar", aaveCfg.bar); - } - - function initAaveBufferPlan( - D3MInstance memory d3m, - D3MAaveBufferPlanConfig memory aaveCfg - ) internal { - D3MAaveBufferPlanLike plan = D3MAaveBufferPlanLike(d3m.plan); - ADaiLike adai = ADaiLike(aaveCfg.adai); - - // Sanity checks - require(plan.adai() == address(adai), "Plan adai mismatch"); - - plan.file("buffer", aaveCfg.buffer); - } - - function initCompoundRateTargetPlan( - D3MInstance memory d3m, - D3MCompoundRateTargetPlanConfig memory compoundCfg - ) internal { - D3MCompoundRateTargetPlanLike plan = D3MCompoundRateTargetPlanLike(d3m.plan); - CDaiLike cdai = CDaiLike(compoundCfg.cdai); - - // Sanity checks - require(plan.tacks(compoundCfg.tack) == 1, "Plan tack mismatch"); - require(cdai.interestRateModel() == compoundCfg.tack, "CDai tack mismatch"); - require(plan.delegates(compoundCfg.delegate) == 1, "Plan delegate mismatch"); - require(cdai.implementation() == compoundCfg.delegate, "CDai delegate mismatch"); - require(plan.cDai() == address(cdai), "Plan cDai mismatch"); - - plan.file("barb", compoundCfg.barb); - } - - function initOperatorPlan( - D3MInstance memory d3m, - D3MOperatorPlanConfig memory operatorCfg - ) internal { - D3MOperatorPlanLike plan = D3MOperatorPlanLike(d3m.plan); - - plan.file("operator", operatorCfg.operator); - } - -} diff --git a/src/dependencies/dss-direct-deposit/D3MInstance.sol b/src/dependencies/dss-direct-deposit/D3MInstance.sol deleted file mode 100644 index eb3ed5cad..000000000 --- a/src/dependencies/dss-direct-deposit/D3MInstance.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: © 2022 Dai Foundation -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -pragma solidity >=0.8.0; - -struct D3MInstance { - address plan; - address pool; - address oracle; -} diff --git a/src/dependencies/dss-lite-psm/DssLitePsmMigration.sol b/src/dependencies/dss-lite-psm/DssLitePsmMigration.sol deleted file mode 100644 index 3b163ecf3..000000000 --- a/src/dependencies/dss-lite-psm/DssLitePsmMigration.sol +++ /dev/null @@ -1,193 +0,0 @@ -// SPDX-FileCopyrightText: © 2023 Dai Foundation -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -pragma solidity ^0.8.16; - -import {DssInstance} from "dss-test/MCD.sol"; - -struct MigrationConfig { - bytes32 srcPsmKey; // Chainlog key - bytes32 dstPsmKey; // Chainlog key - uint256 srcKeep; // [wad] Min amount of gems to keep - uint256 dstWant; // [wad] Max amount of gems to move -} - -struct MigrationResult { - address srcPsm; - bytes32 srcIlk; - address dstPsm; - bytes32 dstIlk; - uint256 sap; // [wad] Amount of gems migrated -} - -struct SrcPsm { - address psm; - bytes32 ilk; - address gemJoin; - address gem; - uint256 rate; // [ray] - uint256 ink; // [wad] - uint256 art; // [wad] -} - -struct DstPsm { - address psm; - bytes32 ilk; - address gem; - uint256 buf; // [wad] - uint256 rate; // [ray] - uint256 line; // [rad] -} - -interface DssPsmLike { - function gemJoin() external view returns (address); - function ilk() external view returns (bytes32); -} - -interface DssLitePsmLike { - function buf() external view returns (uint256); - function file(bytes32, uint256) external; - function fill() external returns (uint256); - function gem() external view returns (address); - function ilk() external view returns (bytes32); - function rush() external view returns (uint256); - function sellGemNoFee(address, uint256) external returns (uint256); - function to18ConversionFactor() external view returns (uint256); -} - -interface GemJoinLike { - function gem() external view returns (address); - function exit(address, uint256) external; -} - -interface GemLike { - function approve(address, uint256) external; -} - -library DssLitePsmMigration { - /// @dev Workaround to explicitly revert with an arithmetic error. - string internal constant ARITHMETIC_ERROR = string(abi.encodeWithSignature("Panic(uint256)", 0x11)); - - uint256 internal constant RAY = 10 ** 27; - - /// @dev Safely converts `uint256` to `int256`. Reverts if it overflows. - function _int256(uint256 x) internal pure returns (int256 y) { - require((y = int256(x)) >= 0, ARITHMETIC_ERROR); - } - - function _min(uint256 x, uint256 y) internal pure returns (uint256 z) { - z = x < y ? x : y; - } - - /// @dev Returns the difference between `x` and `y` or zero if `x` is lower than `y`. - function _subcap(uint256 x, uint256 y) internal pure returns (uint256 z) { - z = x < y ? 0 : x - y; - } - - /** - * @dev Migrates funds from `src` to `dst`. - * @param dss The DSS instance. - * @param cfg The migration config. - * @return res The state of both PSMs after migration. - */ - function migrate(DssInstance memory dss, MigrationConfig memory cfg) - internal - returns (MigrationResult memory res) - { - SrcPsm memory src; - src.psm = dss.chainlog.getAddress(cfg.srcPsmKey); - src.ilk = DssPsmLike(src.psm).ilk(); - src.gemJoin = DssPsmLike(src.psm).gemJoin(); - src.gem = GemJoinLike(src.gemJoin).gem(); - (, src.rate,,,) = dss.vat.ilks(src.ilk); - (src.ink, src.art) = dss.vat.urns(src.ilk, src.psm); - - DstPsm memory dst; - dst.psm = dss.chainlog.getAddress(cfg.dstPsmKey); - dst.ilk = DssLitePsmLike(dst.psm).ilk(); - dst.gem = DssLitePsmLike(dst.psm).gem(); - dst.buf = DssLitePsmLike(dst.psm).buf(); - (, dst.rate,, dst.line,) = dss.vat.ilks(dst.ilk); - - // Store current params to reset them at the end. - uint256 currentGlobalLine = dss.vat.Line(); - - // --- Sanity checks --- - - require(cfg.srcPsmKey != cfg.dstPsmKey, "DssLitePsmMigration/src-psm-same-key-dst-psm"); - uint256 to18ConversionFactor = DssLitePsmLike(dst.psm).to18ConversionFactor(); - require( - cfg.dstWant == type(uint256).max || cfg.dstWant % to18ConversionFactor == 0, - "DssLitePsmMigration/dst-want-rounding-issue" - ); - require(cfg.srcKeep % to18ConversionFactor == 0, "DssLitePsmMigration/src-keep-rounding-issue"); - - require(src.ink >= src.art, "DssLitePsmMigration/src-ink-lower-than-art"); - require(dst.ilk != src.ilk, "DssLitePsmMigration/invalid-ilk-reuse"); - require(dst.gem == src.gem, "DssLitePsmMigration/dst-src-gem-mismatch"); - // We assume stability fees should be set to zero for both PSMs. - require(src.rate == RAY, "DssLitePsmMigration/invalid-src-ilk-rate"); - require(dst.rate == RAY, "DssLitePsmMigration/invalid-dst-ilk-rate"); - - // --- Funds migration --- - - // 0. Define the base parameters for the migration. - // The actual amount to move is constrained by both `dstWant` and `srcKeep`. - uint256 mink = _min(cfg.dstWant, _subcap(src.ink, cfg.srcKeep)); - // Ensure it does not try to erase more than the existing debt. - uint256 mart = _min(src.art, mink); - - // 1. Grab the collateral from `src.psm` into the executing contract. - dss.vat.grab(src.ilk, src.psm, address(this), address(this), -_int256(mink), -_int256(mart)); - - // 2. Transfer the grabbed collateral to the executing contract. - uint256 srcGemAmt = mink / to18ConversionFactor; - GemJoinLike(src.gemJoin).exit(address(this), srcGemAmt); - - // 3. Set interim params to accommodate the migration. - dss.vat.file("Line", type(uint256).max); - dss.vat.file(dst.ilk, "line", type(uint256).max); - - // 4. Pre-mint enough Dai liquidity to move funds from `src.psm`. - DssLitePsmLike(dst.psm).file("buf", mink); - if (DssLitePsmLike(dst.psm).rush() > 0) { - DssLitePsmLike(dst.psm).fill(); - } - - // 5. Sell the grabbed collateral gems to `dst.psm`. - GemLike(dst.gem).approve(dst.psm, srcGemAmt); - uint256 daiOutWad = DssLitePsmLike(dst.psm).sellGemNoFee(address(this), srcGemAmt); - require(daiOutWad == mink, "DssLitePsmMigration/invalid-dai-amount"); - - // 6. Convert ERC20 Dai into Vat Dai. - dss.dai.approve(address(dss.daiJoin), daiOutWad); - dss.daiJoin.join(address(this), daiOutWad); - - // 7. Erase the bad debt generated by `vat.grab()`. - dss.vat.heal(mart * RAY); - - // 8. Reset the previous params. - dss.vat.file("Line", currentGlobalLine); - dss.vat.file(dst.ilk, "line", dst.line); - DssLitePsmLike(dst.psm).file("buf", dst.buf); - - // 9. Return the result params - res.srcPsm = src.psm; - res.srcIlk = src.ilk; - res.dstPsm = dst.psm; - res.dstIlk = dst.ilk; - res.sap = mink; - } -} diff --git a/src/dependencies/dss-lite-psm/phase-3/DssLitePsmMigrationPhase3.sol b/src/dependencies/dss-lite-psm/phase-3/DssLitePsmMigrationPhase3.sol deleted file mode 100644 index a214f2a60..000000000 --- a/src/dependencies/dss-lite-psm/phase-3/DssLitePsmMigrationPhase3.sol +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-FileCopyrightText: © 2023 Dai Foundation -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -pragma solidity ^0.8.16; - -import {DssInstance} from "dss-test/MCD.sol"; -import {DssLitePsmMigration, MigrationConfig, MigrationResult} from "../DssLitePsmMigration.sol"; - -struct DssLitePsmMigrationConfigPhase3 { - bytes32 dstPsmKey; - uint256 dstBuf; // [wad] - uint256 dstMaxLine; // [rad] - uint256 dstGap; // [rad] - uint256 dstTtl; // seconds - bytes32 srcPsmKey; -} - -interface DssPsmLike { - function file(bytes32, uint256) external; - function ilk() external view returns (bytes32); -} - -interface DssLitePsmLike { - function file(bytes32, uint256) external; - function fill() external returns (uint256); - function rush() external view returns (uint256); -} - -interface AutoLineLike { - function exec(bytes32) external returns (uint256); - function remIlk(bytes32) external; - function setIlk(bytes32, uint256, uint256, uint256) external; -} - -library DssLitePsmMigrationPhase3 { - /** - * @dev Performs the final migration of funds. - * @param dss The MCD instance. - * @param cfg The migration config params. - */ - function migrate(DssInstance memory dss, DssLitePsmMigrationConfigPhase3 memory cfg) internal { - // 1. Migrate all funds to the new PSM. - MigrationResult memory res = DssLitePsmMigration.migrate( - dss, - MigrationConfig({srcPsmKey: cfg.srcPsmKey, dstPsmKey: cfg.dstPsmKey, srcKeep: 0, dstWant: type(uint256).max}) - ); - - // 2. Update auto-line. - AutoLineLike autoLine = AutoLineLike(dss.chainlog.getAddress("MCD_IAM_AUTO_LINE")); - - // 2.1. Remove `srcPsm` from AutoLine. - autoLine.remIlk(res.srcIlk); - - // 2.2. Adjust global line accordingly. - (,,, uint256 srcLine,) = dss.vat.ilks(res.srcIlk); - dss.vat.file(res.srcIlk, "line", 0); - dss.vat.file("Line", dss.vat.Line() - srcLine); - - // 2.3. Update auto-line for `dstPsm` - // Notice: Setting auto-line parameters automatically resets time intervals. - // Effectively, it allows `litePsm` `line` to increase faster than expected. - autoLine.setIlk(res.dstIlk, cfg.dstMaxLine, cfg.dstGap, cfg.dstTtl); - autoLine.exec(res.dstIlk); - - // 3. Set the final params for both PSMs. - DssPsmLike(res.srcPsm).file("tin", 0); - DssPsmLike(res.srcPsm).file("tout", 0); - - DssLitePsmLike(res.dstPsm).file("buf", cfg.dstBuf); - - // 4. Fill `dstPsm` so there is liquidity available immediately. - // Notice: `dstPsm.fill` must be called last because it is constrained by both `cfg.buf` and `cfg.maxLine`. - if (DssLitePsmLike(res.dstPsm).rush() > 0) { - DssLitePsmLike(res.dstPsm).fill(); - } - } -} diff --git a/src/test/config.sol b/src/test/config.sol index 1b919205a..d7c5d92ec 100644 --- a/src/test/config.sol +++ b/src/test/config.sol @@ -96,16 +96,16 @@ contract Config { function setValues(address chief) public { // Add spells if there is a need to test prior to their cast() functions // being called on-chain. They will be executed in order from index 0. - address[] memory prevSpells = new address[](0); - // prevSpells[0] = address(0); + address[] memory prevSpells = new address[](1); + prevSpells[0] = address(0x1a0C39D0dBd66956BAfb347f91F55DA1Da2B8F80); // // Values for spell-specific parameters // spellValues = SpellValues({ - deployed_spell: address(0x1a0C39D0dBd66956BAfb347f91F55DA1Da2B8F80), // populate with deployed spell if deployed - deployed_spell_created: 1728049979, // use `make deploy-info tx=` to obtain the timestamp - deployed_spell_block: 20892581, // use `make deploy-info tx=` to obtain the block number + 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: 0, // use `make deploy-info tx=` to obtain the block number previous_spells: prevSpells, // older spells to ensure are executed first office_hours_enabled: true, // true if officehours is expected to be enabled in the spell expiration_threshold: 30 days // Amount of time before spell expires From b0124eb8b44dea9dcd637d2ae7b1d4471f67c232 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Sat, 12 Oct 2024 09:27:30 +0200 Subject: [PATCH 02/44] add spell content --- Makefile | 2 +- scripts/test-dssspell-forge.sh | 2 +- src/DssSpell.sol | 341 +++++++++++++++++- src/DssSpell.t.base.sol | 2 +- src/dependencies/dss-flappers/FlapperInit.sol | 211 +++++++++++ .../dss-flappers/SplitterInstance.sol | 22 ++ src/dependencies/lockstake/LockstakeInit.sol | 259 +++++++++++++ .../lockstake/LockstakeInstance.sol | 24 ++ src/test/addresses_mainnet.sol | 10 +- src/test/config.sol | 51 ++- 10 files changed, 905 insertions(+), 19 deletions(-) create mode 100644 src/dependencies/dss-flappers/FlapperInit.sol create mode 100644 src/dependencies/dss-flappers/SplitterInstance.sol create mode 100644 src/dependencies/lockstake/LockstakeInit.sol create mode 100644 src/dependencies/lockstake/LockstakeInstance.sol diff --git a/Makefile b/Makefile index 1e2ff795e..7ad42f1f6 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ all :; DAPP_LIBRARIES=' lib/dss-exec-lib/src/DssExecLib.sol:DssExecLib:$(shell cat DssExecLib.address)' \ - DAPP_BUILD_OPTIMIZE=0 DAPP_BUILD_OPTIMIZE_RUNS=200 \ + DAPP_BUILD_OPTIMIZE=1 DAPP_BUILD_OPTIMIZE_RUNS=200 \ DAPP_REMAPPINGS=$$(cat remappings.txt) \ dapp --use solc:0.8.16 build clean :; forge clean diff --git a/scripts/test-dssspell-forge.sh b/scripts/test-dssspell-forge.sh index d4d82c483..26d2f3df8 100755 --- a/scripts/test-dssspell-forge.sh +++ b/scripts/test-dssspell-forge.sh @@ -19,7 +19,7 @@ done DSS_EXEC_LIB=$(< DssExecLib.address) echo "Using DssExecLib at: $DSS_EXEC_LIB" export FOUNDRY_LIBRARIES="lib/dss-exec-lib/src/DssExecLib.sol:DssExecLib:$DSS_EXEC_LIB" -export FOUNDRY_OPTIMIZER=false +export FOUNDRY_OPTIMIZER=true export FOUNDRY_OPTIMIZER_RUNS=200 export FOUNDRY_ROOT_CHAINID=1 diff --git a/src/DssSpell.sol b/src/DssSpell.sol index 0b3a1605e..26ca87111 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -19,6 +19,30 @@ pragma solidity 0.8.16; import "dss-exec-lib/DssExec.sol"; import "dss-exec-lib/DssAction.sol"; +import { DssInstance, MCD } from "dss-test/MCD.sol"; +import { VatAbstract } from "dss-interfaces/dss/VatAbstract.sol"; + +// Note: source code matches https://github.com/makerdao/dss-flappers/blob/95431f3d4da66babf81c6e1138bd05f5ddc5e516/deploy/FlapperInit.sol +import { FlapperInit, FarmConfig } from "src/dependencies/dss-flappers/FlapperInit.sol"; + +// Note: source code matches https://github.com/makerdao/lockstake/blob/7c71318623f5d6732457fd0c247a1f1760960011/deploy/LockstakeInit.sol +import { LockstakeInit, LockstakeConfig } from "src/dependencies/lockstake/LockstakeInit.sol"; +// Note: source code matches https://github.com/makerdao/lockstake/blob/7c71318623f5d6732457fd0c247a1f1760960011/deploy/LockstakeInstance.sol +import { LockstakeInstance } from "src/dependencies/lockstake/LockstakeInstance.sol"; + +interface SkyLike { + function mint(address to, uint256 value) external; +} + +interface RwaLiquidationOracleLike { + function cull(bytes32 ilk, address urn) external; + function tell(bytes32 ilk) external; +} + +interface ProxyLike { + function exec(address target, bytes calldata args) external payable returns (bytes memory out); +} + contract DssSpellAction is DssAction { // Provides a descriptive tag for bot consumption // This should be modified weekly to provide a summary of the actions @@ -31,6 +55,15 @@ contract DssSpellAction is DssAction { return true; } + // Note: by the previous convention it should be a comma-separated list of DAO resolutions IPFS hashes + string public constant dao_resolutions = "QmYJUvw5xbAJmJknG2xUKDLe424JSTWQQhbJCnucRRjUv7"; + + // ---------- Math ---------- + uint256 internal constant MILLION = 10 ** 6; + uint256 internal constant WAD = 10 ** 18; + uint256 internal constant RAY = 10 ** 27; + uint256 internal constant RAD = 10 ** 45; + // ---------- Rates ---------- // Many of the settings that change weekly rely on the rate accumulator // described at https://docs.makerdao.com/smart-contract-modules/rates-module @@ -42,8 +75,314 @@ contract DssSpellAction is DssAction { // https://ipfs.io/ipfs/QmVp4mhhbwWGTfbh2BzwQB9eiBrQBKiqcPRZCaAxNUaar6 // // uint256 internal constant X_PCT_RATE = ; + uint256 internal constant TWELVE_PCT_RATE = 1000000003593629043335673582; + + // ---------- Contracts ---------- + address internal immutable MCD_VAT = DssExecLib.vat(); + address internal immutable MIP21_LIQUIDATION_ORACLE = DssExecLib.getChangelogAddress("MIP21_LIQUIDATION_ORACLE"); + address internal immutable RWA007_A_URN = DssExecLib.getChangelogAddress("RWA007_A_URN"); + address internal immutable RWA014_A_URN = DssExecLib.getChangelogAddress("RWA014_A_URN"); + address internal immutable PIP_MKR = DssExecLib.getChangelogAddress("PIP_MKR"); + address internal immutable VOTE_DELEGATE_PROXY_FACTORY = DssExecLib.getChangelogAddress("VOTE_DELEGATE_PROXY_FACTORY"); + address internal immutable MCD_SPLIT = DssExecLib.getChangelogAddress("MCD_SPLIT"); + address internal immutable MCD_VOW = DssExecLib.getChangelogAddress("MCD_VOW"); + address internal immutable USDS_JOIN = DssExecLib.getChangelogAddress("USDS_JOIN"); + address internal immutable USDS = DssExecLib.getChangelogAddress("USDS"); + address internal immutable MCD_GOV = DssExecLib.getChangelogAddress("MCD_GOV"); + address internal immutable MKR_SKY = DssExecLib.getChangelogAddress("MKR_SKY"); + address internal immutable SKY = DssExecLib.getChangelogAddress("SKY"); + address internal constant NEW_PIP_MKR = 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b; + address internal constant VOTE_DELEGATE_FACTORY = 0xC3D809E87A2C9da4F6d98fECea9135d834d6F5A0; + address internal constant REWARDS_LSMKR_USDS = 0x92282235a39bE957fF1f37619fD22A9aE5507CB1; + address internal constant LOCKSTAKE_MKR = 0xb4e0e45e142101dC3Ed768bac219fC35EDBED295; + address internal constant LOCKSTAKE_ENGINE = 0x2b16C07D5fD5cC701a0a871eae2aad6DA5fc8f12; + address internal constant LOCKSTAKE_CLIP = 0xA85621D35cAf9Cf5C146D2376Ce553D7B78A6239; + address internal constant LOCKSTAKE_CLIP_CALC = 0xf13cF3b39823CcfaE6C2354dA56416C80768474e; + + // ---------- Wallets ---------- + address internal constant AAVE_V3_TREASURY = 0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c; + address internal constant AIRDROP_FUND = 0x14D98650d46BF7679BBD05D4f615A1547C87Bf68; + + // ---------- Spark Proxy Spell ---------- + // Spark Proxy: https://github.com/marsfoundation/sparklend-deployments/blob/bba4c57d54deb6a14490b897c12a949aa035a99b/script/output/1/primary-sce-latest.json#L2 + address internal constant SPARK_PROXY = 0x3300f198988e4C9C63F75dF86De36421f06af8c4; + address internal constant SPARK_SPELL = 0x74b3D0E74f2711f30442536832D7fBCB0F42C195; + + function actions() public override { + // Note: multple actions in the spell depend on DssInstance + DssInstance memory dss = MCD.loadFromChainlog(DssExecLib.LOG); + + // ---------- Setup new MkrOsm ---------- + + // Authorize MkrOsm at 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b to read MKR oracle price from PIP_MKR using the following parameters: + // Authorize MkrOSm with address _oracle: PIP_MKR from chainlog + // Authorize MkrOSm with address _reader: 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b + DssExecLib.addReaderToWhitelist(PIP_MKR, NEW_PIP_MKR); + + // Set MkrOsm at 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b as "PIP_MKR" in the chainlog using the following parameters: + // Set MkrOsm with bytes32 _key: "PIP_MKR" + // Set MkrOsm with address _val: 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b + DssExecLib.setChangelogAddress("PIP_MKR", NEW_PIP_MKR); + + // ---------- Setup new VoteDelegateFactory ---------- + + // Rename "VOTE_DELEGATE_PROXY_FACTORY" to "VOTE_DELEGATE_FACTORY_LEGACY" in chainlog with the following parameters: + // Rename chainlog item address with bytes32 _key: "VOTE_DELEGATE_PROXY_FACTORY" + dss.chainlog.removeAddress("VOTE_DELEGATE_PROXY_FACTORY"); + + // Rename chainlog item with bytes32 _key: "VOTE_DELEGATE_FACTORY_LEGACY" + // Rename chainlog item with address _val (VOTE_DELEGATE_PROXY_FACTORY from chainlog) + DssExecLib.setChangelogAddress("VOTE_DELEGATE_FACTORY_LEGACY", VOTE_DELEGATE_PROXY_FACTORY); + + // Set "VOTE_DELEGATE_FACTORY" in the chainlog to 0xC3D809E87A2C9da4F6d98fECea9135d834d6F5A0 with the following parameters: + // Set new chainlog item with bytes32 _key: "VOTE_DELEGATE_FACTORY" + // Set new chainlog item with address _val: 0xC3D809E87A2C9da4F6d98fECea9135d834d6F5A0 + DssExecLib.setChangelogAddress("VOTE_DELEGATE_FACTORY", VOTE_DELEGATE_FACTORY); + + // ---------- Setup Lockstake Engine ---------- + + // SBE Parameter Changes + // Note: this is a subheading, actual instructions are below + + // Decrease splitter "burn" rate by 30% from 100% to 70% with the following parameters: + // Decrease splitter "burn" with address _base: MCD_SPLIT from chainlog + // Decrease splitter "burn" with bytes32 _what: "burn" + // Decrease splitter "burn" with uint256 _amt: 70% + DssExecLib.setValue(MCD_SPLIT, "burn", 70 * WAD / 100); + + // Increase vow.hump by 5 million DAI, from 55 million DAI to 60 million DAI + DssExecLib.setValue(MCD_VOW, "hump", 60 * MILLION * RAD); + + // Increase splitter.hop by 4,014 seconds, from 11,635 seconds to 15,649 seconds. + DssExecLib.setValue(MCD_SPLIT, "hop", 15_649); + + // Set Flapper farm by calling FlapperInit.setFarm with the following parameters: + FlapperInit.setFarm( + + // Note: FlapperInit.setFarm requires DssInstance + dss, + + // Set Flapper farm with address farm_ : 0x92282235a39bE957fF1f37619fD22A9aE5507CB1 + REWARDS_LSMKR_USDS, + + FarmConfig({ + // Set Flapper farm with address splitter: MCD_SPLIT from chainlog + splitter: MCD_SPLIT, + + // Set Flapper farm with address usdsJoin: USDS_JOIN from chainlog + usdsJoin: USDS_JOIN, + + // Set Flapper farm with uint256 hop: 15,649 + hop: 15_649 seconds, + + // Set Flapper farm with bytes32 prevChainlogKey: bytes32(0) + prevChainlogKey: bytes32(0), + + // Set Flapper farm with bytes32 chainlogKey: REWARDS_LSMKR_USDS + chainlogKey: "REWARDS_LSMKR_USDS" + }) + ); + + // "Under the hood" actions for setting flapper: + // LsMkrUsdsFarm will be set as "farm" in MCD_SPLIT + // MCD_SPLIT will be set as "rewardsDistribution" in LsMkrUsdsFarm + // Provided "hop" will be set as "rewardsDuration" in LsMkrUsdsFarm + // New chainlog key REWARDS_LSMKR_USDS will be added + // Provided "hop" will be set as "rewardsDuration" in LsMkrUsdsFarm + // Note: above instructions are taken inside FlapperInit.setFarm method + + // Note: prepare "farms" variable used inside Lockstake init call below + address[] memory farms = new address[](1); + farms[0] = REWARDS_LSMKR_USDS; + + // Init Lockstake Engine by calling LockstakeInit.initLockstake with the following parameters: + LockstakeInit.initLockstake( + + // Note: LockstakeInit.initLockstake requires DssInstance + dss, + + LockstakeInstance({ + // Init Lockstake Engine with address lsmkr: 0xb4e0e45e142101dC3Ed768bac219fC35EDBED295 + lsmkr: LOCKSTAKE_MKR, + + // Init Lockstake Engine with address engine: 0x2b16C07D5fD5cC701a0a871eae2aad6DA5fc8f12 + engine: LOCKSTAKE_ENGINE, + + // Init Lockstake Engine with address clipper: 0xA85621D35cAf9Cf5C146D2376Ce553D7B78A6239 + clipper: LOCKSTAKE_CLIP, + + // Init Lockstake Engine with address clipperCalc: 0xf13cF3b39823CcfaE6C2354dA56416C80768474e + clipperCalc: LOCKSTAKE_CLIP_CALC + }), + + LockstakeConfig({ + // Init Lockstake Engine with bytes32 ilk: "LSE-MKR-A" + ilk: "LSE-MKR-A", + + // Init Lockstake Engine with address voteDelegateFactory: 0xC3D809E87A2C9da4F6d98fECea9135d834d6F5A0 + voteDelegateFactory: VOTE_DELEGATE_FACTORY, + + // Init Lockstake Engine with address usdsJoin: USDS_JOIN from chainlog + usdsJoin: USDS_JOIN, + + // Init Lockstake Engine with address usds: USDS from chainlog + usds: USDS, - function actions() public override {} + // Init Lockstake Engine with address mkr: MCD_GOV from chainlog + mkr: MCD_GOV, + + // Init Lockstake Engine with address mkrSky: MKR_SKY from chainlog + mkrSky: MKR_SKY, + + // Init Lockstake Engine with address sky: SKY from chainlog + sky: SKY, + + // Init Lockstake Engine with address[] farms: 0x92282235a39bE957fF1f37619fD22A9aE5507CB1 + farms: farms, + + // Init Lockstake Engine with uint256 fee: 5% + fee: 5 * WAD / 100, + + // Init Lockstake Engine with uint256 maxLine: 20 million DAI + maxLine: 20 * MILLION * RAD, + + // Init Lockstake Engine with uint256 gap: 5 million + gap: 5 * MILLION * RAD, + + // Init Lockstake Engine with uint256 ttl: 16 hours + ttl: 16 hours, + + // Init Lockstake Engine with uint256 dust: 30,000 DAI + dust: 30_000 * RAD, + + // Init Lockstake Engine with uint256 duty: 12% + duty: TWELVE_PCT_RATE, + + // Init Lockstake Engine with uint256 mat: 200% + mat: 200 * RAY / 100, + + // Init Lockstake Engine with uint256 buf: 1.20 + buf: 120 * RAY / 100, + + // Init Lockstake Engine with uint256 tail: 6,000 seconds + tail: 6_000 seconds, + + // Init Lockstake Engine with uint256 cusp: 0.40 + cusp: 40 * RAY / 100, + + // Init Lockstake Engine with uint256 chip: 0.1% + chip: 1 * WAD / 1000, + + // Init Lockstake Engine with uint256 tip: 300 DAI + tip: 300 * RAD, + + // Init Lockstake Engine with uint256 stopped: 0 + stopped: 0, + + // Init Lockstake Engine with uint256 chop: 8% + chop: 108 * WAD / 100, + + // Init Lockstake Engine with uint256 hole: 3 million DAI + hole: 3 * MILLION * RAD, + + // Init Lockstake Engine with uint256 tau: 0 + tau: 0, + + // Init Lockstake Engine with uint256 cut: 0.99 + cut: 99 * RAY / 100, + + // Init Lockstake Engine with uint256 step: 60 seconds + step: 60 seconds, + + // Init Lockstake Engine with bool lineMom: true + lineMom: true, + + // Init Lockstake Engine with uint256 tolerance: 0.5 + tolerance: 5 * RAY / 10, + + // Init Lockstake Engine with string name: "LOCKSTAKE" + name: "LOCKSTAKE", + + // Init Lockstake Engine with string symbol: "LMKR" + symbol: "LMKR" + }) + ); + + // "Under the hood" actions for Init Lockstake Engine: + // New collateral type "LSE-MKR-A" will be added to "vat", "jug", "spotter", "dog" contracts + // New collateral type "LSE-MKR-A" will be added to LINE_MOM + // New collateral type "LSE-MKR-A" will be added to auto-line using provided maxLine, gap and ttl + // New collateral type "LSE-MKR-A" will be added to ILK_REGISTRY with provided values ("name", "symbol") and the new ilk class 7 + // New MKR OSM will allow MCD_SPOT, CLIPPER_MOM, OSM_MOM, MCD_END and LockstakeClipper to access its price + // PIP_MKR will be added to OSM_MOM + // LockstakeClipper will be configured using provided values ("buf", "tail", "cusp", "chip", "tip", "stopped", "clip", "tolerance") + // StairstepExponentialDecrease calc contract will be configured using provided values ("cut", "step") + // The LsMkrUsdsFarm will be added to the LockstakeEngine as a first farm + // LockstakeEngine will be authorized to access "vat" + // LockstakeClipper will be authorized to access "vat" and LockstakeEngine + // CLIPPER_MOM, MCD_DOG and MCD_END will be authorized to access LockstakeClipper + // New chainlog keys LOCKSTAKE_MKR, LOCKSTAKE_ENGINE, LOCKSTAKE_CLIP and LOCKSTAKE_CLIP_CALC will be added + // Note: above instructions are taken inside FlapperInit.setFarm method + + // ---------- Fund Early Bird Rewards Multisig ---------- + + // Mint 27,222,832.80 SKY to 0x14D98650d46BF7679BBD05D4f615A1547C87Bf68 + SkyLike(SKY).mint(AIRDROP_FUND, 28_220_926 * WAD); + + // ---------- Lower Deprecated RWA Debt Ceilings ---------- + + // Note: we need extra variables to calculate the decrease of the global debt ceiling + uint256 line; + uint256 globalLineReduction = 0; + + // Remove RWA007-A from Debt Ceiling Instant Access Module + DssExecLib.removeIlkFromAutoLine("RWA007-A"); + + // Set RWA007-A Debt Ceiling to 0 + (,,,line,) = VatAbstract(MCD_VAT).ilks("RWA007-A"); + globalLineReduction += line; + DssExecLib.setIlkDebtCeiling("RWA007-A", 0); + + // Initiate RWA007-A soft liquidation by calling `tell()` + RwaLiquidationOracleLike(MIP21_LIQUIDATION_ORACLE).tell("RWA007-A"); + + // Write-off the debt of RWA007-A and set its oracle price to 0 by calling `cull()` + RwaLiquidationOracleLike(MIP21_LIQUIDATION_ORACLE).cull("RWA007-A", RWA007_A_URN); + + // Reduce RWA014-A Debt Ceiling by 1.5 billion Dai from 1.5 billion Dai to 0 + (,,,line,) = VatAbstract(MCD_VAT).ilks("RWA014-A"); + globalLineReduction += line; + DssExecLib.setIlkDebtCeiling("RWA014-A", 0); + + // Initiate RWA014-A soft liquidation by calling `tell()` + RwaLiquidationOracleLike(MIP21_LIQUIDATION_ORACLE).tell("RWA014-A"); + + // Write-off the debt of RWA014-A and set its oracle price to 0 by calling `cull()` + RwaLiquidationOracleLike(MIP21_LIQUIDATION_ORACLE).cull("RWA014-A", RWA014_A_URN); + + // Note: decrease global line + VatAbstract(MCD_VAT).file("Line", VatAbstract(MCD_VAT).Line() - globalLineReduction); + + // ---------- Pinwheel DAO Resolution ---------- + + // Approve DAO Resolution at QmYJUvw5xbAJmJknG2xUKDLe424JSTWQQhbJCnucRRjUv7 + // Note: see `dao_resolutions` public variable declared above + + // ---------- AAVE Revenue Share Payment ---------- + + // AAVE Revenue Share - 234089 DAI - 0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c + DssExecLib.sendPaymentFromSurplusBuffer(AAVE_V3_TREASURY, 234_089); + + // ---------- Spark Spell ---------- + + // Execute Spark Proxy Spell at 0x74b3D0E74f2711f30442536832D7fBCB0F42C195 + ProxyLike(SPARK_PROXY).exec(SPARK_SPELL, abi.encodeWithSignature("execute()")); + + // ---------- Chainlog bump ---------- + + // Note: we have to increase minor chainlog version as new keys are added + DssExecLib.setChangelogVersion("1.19.2"); + } } contract DssSpell is DssExec { diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index e9377220e..281ddc8a4 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -718,7 +718,7 @@ contract DssSpellTestBase is Config, DssTest { } if (values.collaterals[ilk].liqType == "clip") { { - assertEq(reg.class(ilk), 1, _concat("TestError/reg-class-", ilk)); + assertTrue(reg.class(ilk) == 1 || reg.class(ilk) == 7, _concat("TestError/reg-class-", ilk)); (bool ok, bytes memory val) = reg.xlip(ilk).call(abi.encodeWithSignature("dog()")); assertTrue(ok, _concat("TestError/reg-xlip-dog-", ilk)); assertEq(abi.decode(val, (address)), address(dog), _concat("TestError/reg-xlip-dog-", ilk)); diff --git a/src/dependencies/dss-flappers/FlapperInit.sol b/src/dependencies/dss-flappers/FlapperInit.sol new file mode 100644 index 000000000..4d7475efe --- /dev/null +++ b/src/dependencies/dss-flappers/FlapperInit.sol @@ -0,0 +1,211 @@ +// SPDX-FileCopyrightText: © 2023 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity >=0.8.0; + +import { DssInstance } from "dss-test/MCD.sol"; +import { SplitterInstance } from "./SplitterInstance.sol"; + +interface FlapperUniV2Like { + function pip() external view returns (address); + function spotter() external view returns (address); + function usds() external view returns (address); + function gem() external view returns (address); + function receiver() external view returns (address); + function pair() external view returns (address); + function rely(address) external; + function file(bytes32, uint256) external; + function file(bytes32, address) external; +} + +interface SplitterMomLike { + function splitter() external view returns (address); + function setAuthority(address) external; +} + +interface OracleWrapperLike { + function pip() external view returns (address); + function divisor() external view returns (uint256); +} + +interface PipLike { + function kiss(address) external; +} + +interface PairLike { + function token0() external view returns (address); + function token1() external view returns (address); +} + +interface UsdsJoinLike { + function dai() external view returns (address); // TODO: Replace when new join is ready by the new getter +} + +interface SplitterLike { + function live() external view returns (uint256); + function vat() external view returns (address); + function usdsJoin() external view returns (address); + function hop() external view returns (uint256); + function rely(address) external; + function file(bytes32, uint256) external; + function file(bytes32, address) external; +} + +interface FarmLike { + function rewardsToken() external view returns (address); + function setRewardsDistribution(address) external; + function setRewardsDuration(uint256) external; +} + +struct FlapperUniV2Config { + uint256 want; + address pip; + address pair; + address usds; + address splitter; + bytes32 prevChainlogKey; + bytes32 chainlogKey; +} + +struct FarmConfig { + address splitter; + address usdsJoin; + uint256 hop; + bytes32 prevChainlogKey; + bytes32 chainlogKey; +} + +struct SplitterConfig { + uint256 hump; + uint256 bump; + uint256 hop; + uint256 burn; + address usdsJoin; + bytes32 splitterChainlogKey; + bytes32 prevMomChainlogKey; + bytes32 momChainlogKey; +} + +library FlapperInit { + uint256 constant WAD = 10 ** 18; + uint256 constant RAY = 10 ** 27; + + function initFlapperUniV2( + DssInstance memory dss, + address flapper_, + FlapperUniV2Config memory cfg + ) internal { + FlapperUniV2Like flapper = FlapperUniV2Like(flapper_); + + // Sanity checks + require(flapper.spotter() == address(dss.spotter), "Flapper spotter mismatch"); + require(flapper.usds() == cfg.usds, "Flapper usds mismatch"); + require(flapper.pair() == cfg.pair, "Flapper pair mismatch"); + require(flapper.receiver() == dss.chainlog.getAddress("MCD_PAUSE_PROXY"), "Flapper receiver mismatch"); + + PairLike pair = PairLike(flapper.pair()); + (address pairUsds, address pairGem) = pair.token0() == cfg.usds ? (pair.token0(), pair.token1()) + : (pair.token1(), pair.token0()); + require(pairUsds == cfg.usds, "Usds mismatch"); + require(pairGem == flapper.gem(), "Gem mismatch"); + + require(cfg.want >= WAD * 90 / 100, "want too low"); + + flapper.file("want", cfg.want); + flapper.file("pip", cfg.pip); + flapper.rely(cfg.splitter); + + SplitterLike(cfg.splitter).file("flapper", flapper_); + + if (cfg.prevChainlogKey != bytes32(0)) dss.chainlog.removeAddress(cfg.prevChainlogKey); + dss.chainlog.setAddress(cfg.chainlogKey, flapper_); + } + + function initDirectOracle(address flapper) internal { + PipLike(FlapperUniV2Like(flapper).pip()).kiss(flapper); + } + + function initOracleWrapper( + DssInstance memory dss, + address wrapper_, + uint256 divisor, + bytes32 clKey + ) internal { + OracleWrapperLike wrapper = OracleWrapperLike(wrapper_); + require(wrapper.divisor() == divisor, "Wrapper divisor mismatch"); // Sanity check + PipLike(wrapper.pip()).kiss(wrapper_); + dss.chainlog.setAddress(clKey, wrapper_); + } + + function setFarm( + DssInstance memory dss, + address farm_, + FarmConfig memory cfg + ) internal { + FarmLike farm = FarmLike(farm_); + SplitterLike splitter = SplitterLike(cfg.splitter); + + require(farm.rewardsToken() == UsdsJoinLike(cfg.usdsJoin).dai(), "Farm rewards not usds"); + // Staking token is checked in the Lockstake script + + // The following two checks enforce the initSplitter function has to be called first + require(cfg.hop >= 5 minutes, "hop too low"); + require(cfg.hop == splitter.hop(), "hop mismatch"); + + splitter.file("farm", farm_); + + farm.setRewardsDistribution(cfg.splitter); + farm.setRewardsDuration(cfg.hop); + + if (cfg.prevChainlogKey != bytes32(0)) dss.chainlog.removeAddress(cfg.prevChainlogKey); + dss.chainlog.setAddress(cfg.chainlogKey, farm_); + } + + function initSplitter( + DssInstance memory dss, + SplitterInstance memory splitterInstance, + SplitterConfig memory cfg + ) internal { + SplitterLike splitter = SplitterLike(splitterInstance.splitter); + SplitterMomLike mom = SplitterMomLike(splitterInstance.mom); + + // Sanity checks + require(splitter.live() == 1, "Splitter not live"); + require(splitter.vat() == address(dss.vat), "Splitter vat mismatch"); + require(splitter.usdsJoin() == cfg.usdsJoin, "Splitter usdsJoin mismatch"); + require(mom.splitter() == splitterInstance.splitter, "Mom splitter mismatch"); + + require(cfg.hump > 0, "hump too low"); + require(cfg.bump % RAY == 0, "bump not multiple of RAY"); + require(cfg.hop >= 5 minutes, "hop too low"); + require(cfg.burn <= WAD, "burn too high"); + + splitter.file("hop", cfg.hop); + splitter.file("burn", cfg.burn); + splitter.rely(address(mom)); + splitter.rely(address(dss.vow)); + + dss.vow.file("flapper", splitterInstance.splitter); + dss.vow.file("hump", cfg.hump); + dss.vow.file("bump", cfg.bump); + + mom.setAuthority(dss.chainlog.getAddress("MCD_ADM")); + + dss.chainlog.setAddress(cfg.splitterChainlogKey, splitterInstance.splitter); + if (cfg.prevMomChainlogKey != bytes32(0)) dss.chainlog.removeAddress(cfg.prevMomChainlogKey); + dss.chainlog.setAddress(cfg.momChainlogKey, address(mom)); + } +} diff --git a/src/dependencies/dss-flappers/SplitterInstance.sol b/src/dependencies/dss-flappers/SplitterInstance.sol new file mode 100644 index 000000000..bb61fb721 --- /dev/null +++ b/src/dependencies/dss-flappers/SplitterInstance.sol @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: © 2023 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity >=0.8.0; + +struct SplitterInstance { + address splitter; + address mom; +} diff --git a/src/dependencies/lockstake/LockstakeInit.sol b/src/dependencies/lockstake/LockstakeInit.sol new file mode 100644 index 000000000..57809d25d --- /dev/null +++ b/src/dependencies/lockstake/LockstakeInit.sol @@ -0,0 +1,259 @@ +// SPDX-FileCopyrightText: © 2023 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity >=0.8.0; + +import { DssInstance } from "dss-test/MCD.sol"; +import { LockstakeInstance } from "./LockstakeInstance.sol"; + +interface LockstakeMkrLike { + function rely(address) external; +} + +interface LockstakeEngineLike { + function voteDelegateFactory() external view returns (address); + function vat() external view returns (address); + function usdsJoin() external view returns (address); + function usds() external view returns (address); + function ilk() external view returns (bytes32); + function mkr() external view returns (address); + function lsmkr() external view returns (address); + function fee() external view returns (uint256); + function mkrSky() external view returns (address); + function sky() external view returns (address); + function rely(address) external; + function file(bytes32, address) external; + function file(bytes32, uint256) external; + function addFarm(address) external; +} + +interface LockstakeClipperLike { + function vat() external view returns (address); + function dog() external view returns (address); + function spotter() external view returns (address); + function engine() external view returns (address); + function ilk() external view returns (bytes32); + function rely(address) external; + function file(bytes32, address) external; + function file(bytes32, uint256) external; + function upchost() external; +} + +interface PipLike { + function kiss(address) external; + function rely(address) external; +} + +interface CalcLike { + function file(bytes32, uint256) external; +} + +interface AutoLineLike { + function setIlk(bytes32, uint256, uint256, uint256) external; +} + +interface OsmMomLike { + function setOsm(bytes32, address) external; +} + +interface LineMomLike { + function addIlk(bytes32) external; +} + +interface ClipperMomLike { + function setPriceTolerance(address, uint256) external; +} + +interface StakingRewardsLike { + function stakingToken() external view returns (address); +} + +interface IlkRegistryLike { + function put( + bytes32 _ilk, + address _join, + address _gem, + uint256 _dec, + uint256 _class, + address _pip, + address _xlip, + string memory _name, + string memory _symbol + ) external; +} + +struct LockstakeConfig { + bytes32 ilk; + address voteDelegateFactory; + address usdsJoin; + address usds; + address mkr; + address mkrSky; + address sky; + address[] farms; + uint256 fee; + uint256 maxLine; + uint256 gap; + uint256 ttl; + uint256 dust; + uint256 duty; + uint256 mat; + uint256 buf; + uint256 tail; + uint256 cusp; + uint256 chip; + uint256 tip; + uint256 stopped; + uint256 chop; + uint256 hole; + uint256 tau; + uint256 cut; + uint256 step; + bool lineMom; + uint256 tolerance; + string name; + string symbol; +} + +library LockstakeInit { + uint256 constant internal RATES_ONE_HUNDRED_PCT = 1000000021979553151239153027; + uint256 constant internal WAD = 10**18; + uint256 constant internal RAY = 10**27; + uint256 constant internal RAD = 10**45; + + function initLockstake( + DssInstance memory dss, + LockstakeInstance memory lockstakeInstance, + LockstakeConfig memory cfg + ) internal { + LockstakeEngineLike engine = LockstakeEngineLike(lockstakeInstance.engine); + LockstakeClipperLike clipper = LockstakeClipperLike(lockstakeInstance.clipper); + CalcLike calc = CalcLike(lockstakeInstance.clipperCalc); + + // Sanity checks + require(engine.voteDelegateFactory() == cfg.voteDelegateFactory, "Engine voteDelegateFactory mismatch"); + require(engine.vat() == address(dss.vat), "Engine vat mismatch"); + require(engine.usdsJoin() == cfg.usdsJoin, "Engine usdsJoin mismatch"); + require(engine.usds() == cfg.usds, "Engine usds mismatch"); + require(engine.ilk() == cfg.ilk, "Engine ilk mismatch"); + require(engine.mkr() == cfg.mkr, "Engine mkr mismatch"); + require(engine.lsmkr() == lockstakeInstance.lsmkr, "Engine lsmkr mismatch"); + require(engine.mkrSky() == cfg.mkrSky, "Engine mkrSky mismatch"); + require(engine.sky() == cfg.sky, "Engine sky mismatch"); + require(clipper.ilk() == cfg.ilk, "Clipper ilk mismatch"); + require(clipper.vat() == address(dss.vat), "Clipper vat mismatch"); + require(clipper.engine() == address(engine), "Clipper engine mismatch"); + require(clipper.dog() == address(dss.dog), "Clipper dog mismatch"); + require(clipper.spotter() == address(dss.spotter), "Clipper spotter mismatch"); + + require(cfg.gap <= cfg.maxLine, "gap greater than max line"); + require(cfg.dust <= cfg.hole, "dust greater than hole"); + require(cfg.duty >= RAY && cfg.duty <= RATES_ONE_HUNDRED_PCT, "duty out of boundaries"); + require(cfg.mat >= RAY && cfg.mat < 10 * RAY, "mat out of boundaries"); + require(cfg.buf >= RAY && cfg.buf < 10 * RAY, "buf out of boundaries"); + require(cfg.cusp < RAY, "cusp negative drop value"); + require(cfg.chip < WAD, "chip equal or greater than 100%"); + require(cfg.tip <= 1_000 * RAD, "tip out of boundaries"); + require(cfg.chop >= WAD && cfg.chop < 2 * WAD, "chop out of boundaries"); + require(cfg.tolerance < RAY, "tolerance equal or greater than 100%"); + + dss.vat.init(cfg.ilk); + dss.vat.file(cfg.ilk, "line", cfg.gap); + dss.vat.file("Line", dss.vat.Line() + cfg.gap); + dss.vat.file(cfg.ilk, "dust", cfg.dust); + dss.vat.rely(address(engine)); + dss.vat.rely(address(clipper)); + + AutoLineLike(dss.chainlog.getAddress("MCD_IAM_AUTO_LINE")).setIlk(cfg.ilk, cfg.maxLine, cfg.gap, cfg.ttl); + + dss.jug.init(cfg.ilk); + dss.jug.file(cfg.ilk, "duty", cfg.duty); + + address pip = dss.chainlog.getAddress("PIP_MKR"); + address clipperMom = dss.chainlog.getAddress("CLIPPER_MOM"); + PipLike(pip).kiss(address(dss.spotter)); + PipLike(pip).kiss(address(clipper)); + PipLike(pip).kiss(clipperMom); + PipLike(pip).kiss(address(dss.end)); + // This assumes pip is a standard Osm sourced by a Median + { + address osmMom = dss.chainlog.getAddress("OSM_MOM"); + PipLike(pip).rely(osmMom); + OsmMomLike(osmMom).setOsm(cfg.ilk, pip); + } + + dss.spotter.file(cfg.ilk, "mat", cfg.mat); + dss.spotter.file(cfg.ilk, "pip", pip); + dss.spotter.poke(cfg.ilk); + + dss.dog.file(cfg.ilk, "clip", address(clipper)); + dss.dog.file(cfg.ilk, "chop", cfg.chop); + dss.dog.file(cfg.ilk, "hole", cfg.hole); + dss.dog.rely(address(clipper)); + + LockstakeMkrLike(lockstakeInstance.lsmkr).rely(address(engine)); + + engine.file("jug", address(dss.jug)); + engine.file("fee", cfg.fee); + for (uint256 i = 0; i < cfg.farms.length; i++) { + require(StakingRewardsLike(cfg.farms[i]).stakingToken() == lockstakeInstance.lsmkr, "Farm staking token mismatch"); + engine.addFarm(cfg.farms[i]); + } + engine.rely(address(clipper)); + + clipper.file("buf", cfg.buf); + clipper.file("tail", cfg.tail); + clipper.file("cusp", cfg.cusp); + clipper.file("chip", cfg.chip); + clipper.file("tip", cfg.tip); + clipper.file("stopped", cfg.stopped); + clipper.file("vow", address(dss.vow)); + clipper.file("calc", address(calc)); + clipper.upchost(); + clipper.rely(address(dss.dog)); + clipper.rely(address(dss.end)); + clipper.rely(clipperMom); + + if (cfg.tau > 0) calc.file("tau", cfg.tau); + if (cfg.cut > 0) calc.file("cut", cfg.cut); + if (cfg.step > 0) calc.file("step", cfg.step); + + if (cfg.lineMom) { + LineMomLike(dss.chainlog.getAddress("LINE_MOM")).addIlk(cfg.ilk); + } + + if (cfg.tolerance > 0) { + ClipperMomLike(clipperMom).setPriceTolerance(address(clipper), cfg.tolerance); + } + + IlkRegistryLike(dss.chainlog.getAddress("ILK_REGISTRY")).put( + cfg.ilk, + address(0), + cfg.mkr, + 18, + 7, // New class + pip, + address(clipper), + cfg.name, + cfg.symbol + ); + + dss.chainlog.setAddress("LOCKSTAKE_MKR", lockstakeInstance.lsmkr); + dss.chainlog.setAddress("LOCKSTAKE_ENGINE", address(engine)); + dss.chainlog.setAddress("LOCKSTAKE_CLIP", address(clipper)); + dss.chainlog.setAddress("LOCKSTAKE_CLIP_CALC", address(calc)); + } +} diff --git a/src/dependencies/lockstake/LockstakeInstance.sol b/src/dependencies/lockstake/LockstakeInstance.sol new file mode 100644 index 000000000..d249711d0 --- /dev/null +++ b/src/dependencies/lockstake/LockstakeInstance.sol @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: © 2023 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity >=0.8.0; + +struct LockstakeInstance { + address lsmkr; + address engine; + address clipper; + address clipperCalc; +} diff --git a/src/test/addresses_mainnet.sol b/src/test/addresses_mainnet.sol index a6d9d3ecb..87f3bb6bd 100644 --- a/src/test/addresses_mainnet.sol +++ b/src/test/addresses_mainnet.sol @@ -33,7 +33,8 @@ contract Addresses { addr["GOV_GUARD"] = 0x6eEB68B2C7A918f36B78E2DB80dcF279236DDFb8; addr["MCD_ADM"] = 0x0a3f6849f78076aefaDf113F5BED87720274dDC0; addr["VOTE_PROXY_FACTORY"] = 0x6FCD258af181B3221073A96dD90D1f7AE7eEc408; - addr["VOTE_DELEGATE_PROXY_FACTORY"] = 0xD897F108670903D1d6070fcf818f9db3615AF272; + addr["VOTE_DELEGATE_FACTORY_LEGACY"] = 0xD897F108670903D1d6070fcf818f9db3615AF272; + addr["VOTE_DELEGATE_FACTORY"] = 0xC3D809E87A2C9da4F6d98fECea9135d834d6F5A0; addr["MCD_VAT"] = 0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B; addr["MCD_JUG"] = 0x19c0976f590D67707E62397C87829d896Dc0f1F1; addr["MCD_DOG"] = 0x135954d155898D42C90D2a57824C690e0c7BEf1B; @@ -73,7 +74,7 @@ contract Addresses { addr["CDP_REGISTRY"] = 0xBe0274664Ca7A68d6b5dF826FB3CcB7c620bADF3; addr["MCD_CROPPER"] = 0x8377CD01a5834a6EaD3b7efb482f678f2092b77e; addr["MCD_CROPPER_IMP"] = 0xaFB21A0e9669cdbA539a4c91Bf6B94c5F013c0DE; - addr["PIP_MKR"] = 0xdbBe5e9B1dAa91430cF0772fCEbe53F6c6f137DF; + addr["PIP_MKR"] = 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b; addr["ETH"] = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; addr["PIP_ETH"] = 0x81FE72B5A8d1A857d176C3E7d5Bd2679A9B85763; addr["MCD_JOIN_ETH_A"] = 0x2F0b23f53734252Bda2277357e97e1517d6B042A; @@ -510,7 +511,12 @@ contract Addresses { addr["REWARDS_USDS_SKY"] = 0x0650CAF159C5A49f711e8169D4336ECB9b950275; addr["REWARDS_DIST_USDS_SKY"] = 0x2F0C88e935Db5A60DDA73b0B4EAEef55883896d9; addr["REWARDS_USDS_01"] = 0x10ab606B067C9C461d8893c47C7512472E19e2Ce; + addr["REWARDS_LSMKR_USDS"] = 0x92282235a39bE957fF1f37619fD22A9aE5507CB1; addr["CRON_REWARDS_DIST_JOB"] = 0x6464C34A02DD155dd0c630CE233DD6e21C24F9A5; addr["WRAPPER_USDS_LITE_PSM_USDC_A"] = 0xA188EEC8F81263234dA3622A406892F3D630f98c; + addr["LOCKSTAKE_MKR"] = 0xb4e0e45e142101dC3Ed768bac219fC35EDBED295; + addr["LOCKSTAKE_ENGINE"] = 0x2b16C07D5fD5cC701a0a871eae2aad6DA5fc8f12; + addr["LOCKSTAKE_CLIP"] = 0xA85621D35cAf9Cf5C146D2376Ce553D7B78A6239; + addr["LOCKSTAKE_CLIP_CALC"] = 0xf13cF3b39823CcfaE6C2354dA56416C80768474e; } } diff --git a/src/test/config.sol b/src/test/config.sol index d7c5d92ec..5f3d29dde 100644 --- a/src/test/config.sol +++ b/src/test/config.sol @@ -96,8 +96,8 @@ contract Config { function setValues(address chief) public { // Add spells if there is a need to test prior to their cast() functions // being called on-chain. They will be executed in order from index 0. - address[] memory prevSpells = new address[](1); - prevSpells[0] = address(0x1a0C39D0dBd66956BAfb347f91F55DA1Da2B8F80); + address[] memory prevSpells = new address[](0); + // prevSpells[0] = address(0); // // Values for spell-specific parameters @@ -122,10 +122,10 @@ contract Config { afterSpell.vow_dump = 250; // In whole Dai units afterSpell.vow_sump = 50 * THOUSAND; // In whole Dai units afterSpell.vow_bump = 25 * THOUSAND; // In whole Dai units - afterSpell.vow_hump_min = 55 * MILLION; // In whole Dai units - afterSpell.vow_hump_max = 55 * MILLION; // In whole Dai units - afterSpell.split_hop = 11_635 seconds; // In seconds - afterSpell.split_burn = 100_00; // In basis points + afterSpell.vow_hump_min = 60 * MILLION; // In whole Dai units + afterSpell.vow_hump_max = 60 * MILLION; // In whole Dai units + afterSpell.split_hop = 15_649 seconds; // In seconds + afterSpell.split_burn = 70_00; // In basis points afterSpell.flap_want = 9800; // In basis points afterSpell.dog_Hole = 150 * MILLION; // In whole Dai units afterSpell.esm_min = 300 * THOUSAND; // In whole MKR units @@ -140,8 +140,8 @@ contract Config { afterSpell.vest_mkr_cap = 2_220 * WAD / 365 days; // In WAD MKR per second afterSpell.vest_sky_cap = 800 * MILLION * WAD / 365 days; // In WAD SKY per second afterSpell.sky_mkr_rate = 24_000; // In whole SKY/MKR units - afterSpell.ilk_count = 67; // Num expected in system - afterSpell.chainlog_version = "1.19.1"; // String expected in system + afterSpell.ilk_count = 68; // Num expected in system + afterSpell.chainlog_version = "1.19.2"; // String expected in system // // Values for all collateral @@ -1198,10 +1198,10 @@ contract Config { offboarding: false }); afterSpell.collaterals["RWA007-A"] = CollateralValues({ - aL_enabled: true, - aL_line: 3 * BILLION, - aL_gap: 50 * MILLION, - aL_ttl: 24 hours, + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, line: 0, dust: 0, pct: 0, @@ -1377,7 +1377,7 @@ contract Config { aL_line: 0, aL_gap: 0, aL_ttl: 0, - line: 1_500 * MILLION, + line: 0, dust: 0, pct: 0, mat: 100_00, @@ -1822,5 +1822,30 @@ contract Config { calc_cut: 0, offboarding: false }); + afterSpell.collaterals["LSE-MKR-A"] = CollateralValues({ + aL_enabled: true, + aL_line: 20_000_000, + aL_gap: 5_000_000, + aL_ttl: 16 hours, + line: 0, + dust: 30_000, + pct: 12_00, + mat: 200_00, + liqType: "clip", + liqOn: true, + chop: 8_00, + dog_hole: 3 * MILLION, + clip_buf: 120_00, + clip_tail: 100 minutes, + clip_cusp: 40_00, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 50_00, + calc_tau: 0, + calc_step: 60, + calc_cut: 99_00, + offboarding: false + }); } } From 6b04459926545454dd7f76a5d1d1500c6457b54b Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Sun, 13 Oct 2024 23:48:34 +0200 Subject: [PATCH 03/44] update tests --- src/DssSpell.sol | 17 ++-- src/DssSpell.t.sol | 149 +++++++++++++++++++-------------- src/test/addresses_wallets.sol | 3 + src/test/config.sol | 4 +- 4 files changed, 101 insertions(+), 72 deletions(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index 26ca87111..66b1e4115 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -100,8 +100,8 @@ contract DssSpellAction is DssAction { address internal constant LOCKSTAKE_CLIP_CALC = 0xf13cF3b39823CcfaE6C2354dA56416C80768474e; // ---------- Wallets ---------- - address internal constant AAVE_V3_TREASURY = 0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c; - address internal constant AIRDROP_FUND = 0x14D98650d46BF7679BBD05D4f615A1547C87Bf68; + address internal constant AAVE_V3_TREASURY = 0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c; + address internal constant EARLY_BIRD_REWARDS = 0x14D98650d46BF7679BBD05D4f615A1547C87Bf68; // ---------- Spark Proxy Spell ---------- // Spark Proxy: https://github.com/marsfoundation/sparklend-deployments/blob/bba4c57d54deb6a14490b897c12a949aa035a99b/script/output/1/primary-sce-latest.json#L2 @@ -327,7 +327,7 @@ contract DssSpellAction is DssAction { // ---------- Fund Early Bird Rewards Multisig ---------- // Mint 27,222,832.80 SKY to 0x14D98650d46BF7679BBD05D4f615A1547C87Bf68 - SkyLike(SKY).mint(AIRDROP_FUND, 28_220_926 * WAD); + SkyLike(SKY).mint(EARLY_BIRD_REWARDS, 27_222_832.80 ether); // Note: ether is only a keyword helper // ---------- Lower Deprecated RWA Debt Ceilings ---------- @@ -338,9 +338,11 @@ contract DssSpellAction is DssAction { // Remove RWA007-A from Debt Ceiling Instant Access Module DssExecLib.removeIlkFromAutoLine("RWA007-A"); - // Set RWA007-A Debt Ceiling to 0 + // Note: in order to decrease global debt ceiling, we need to fetch current `line` (,,,line,) = VatAbstract(MCD_VAT).ilks("RWA007-A"); globalLineReduction += line; + + // Set RWA007-A Debt Ceiling to 0 DssExecLib.setIlkDebtCeiling("RWA007-A", 0); // Initiate RWA007-A soft liquidation by calling `tell()` @@ -349,9 +351,11 @@ contract DssSpellAction is DssAction { // Write-off the debt of RWA007-A and set its oracle price to 0 by calling `cull()` RwaLiquidationOracleLike(MIP21_LIQUIDATION_ORACLE).cull("RWA007-A", RWA007_A_URN); - // Reduce RWA014-A Debt Ceiling by 1.5 billion Dai from 1.5 billion Dai to 0 + // Note: in order to decrease global debt ceiling, we need to fetch current `line` (,,,line,) = VatAbstract(MCD_VAT).ilks("RWA014-A"); globalLineReduction += line; + + // Reduce RWA014-A Debt Ceiling by 1.5 billion Dai from 1.5 billion Dai to 0 DssExecLib.setIlkDebtCeiling("RWA014-A", 0); // Initiate RWA014-A soft liquidation by calling `tell()` @@ -376,7 +380,8 @@ contract DssSpellAction is DssAction { // ---------- Spark Spell ---------- // Execute Spark Proxy Spell at 0x74b3D0E74f2711f30442536832D7fBCB0F42C195 - ProxyLike(SPARK_PROXY).exec(SPARK_SPELL, abi.encodeWithSignature("execute()")); + // ProxyLike(SPARK_PROXY).exec(SPARK_SPELL, abi.encodeWithSignature("execute()")); + // TODO: update spell address when redeployed // ---------- Chainlog bump ---------- diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index 4bb75cb03..91145219f 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -49,6 +49,11 @@ interface SequencerLike { function hasJob(address job) external view returns (bool); } +interface RwaLiquidationOracleLike { + function good(bytes32 ilk) external view returns (bool); + function ilks(bytes32) external view returns (string memory doc, address pip, uint48 tau, uint48 toc); +} + contract DssSpellTest is DssSpellTestBase { string config; RootDomain rootDomain; @@ -524,45 +529,32 @@ contract DssSpellTest is DssSpellTestBase { struct Payee { address token; address addr; - uint256 amount; + int256 amount; } struct PaymentAmounts { - uint256 dai; - uint256 mkr; - uint256 usds; - uint256 sky; + int256 dai; + int256 mkr; + int256 usds; + int256 sky; } - function testPayments() public skipped { // add the `skipped` modifier to skip + function testPayments() public { // add the `skipped` modifier to skip // For each payment, create a Payee object with: // the address of the transferred token, // the destination address, // the amount to be paid // Initialize the array with the number of payees - Payee[15] memory payees = [ - Payee(address(dai), wallets.addr("BLUE"), 54_167 * WAD), - Payee(address(dai), wallets.addr("CLOAKY"), 20_417 * WAD), - Payee(address(dai), wallets.addr("CLOAKY_KOHLA_2"), 10_000 * WAD), - Payee(address(dai), wallets.addr("CLOAKY_ENNOIA"), 10_000 * WAD), - Payee(address(dai), wallets.addr("JULIACHANG"), 8_333 * WAD), - Payee(address(dai), wallets.addr("BYTERON"), 8_333 * WAD), - Payee(address(dai), wallets.addr("ROCKY"), 8_065 * WAD), - Payee(address(dai), wallets.addr("BONAPUBLICA"), 5_430 * WAD), - Payee(address(mkr), wallets.addr("BLUE"), 13.75 ether), // Note: ether is only a keyword helper - Payee(address(mkr), wallets.addr("CLOAKY"), 12.00 ether), // Note: ether is only a keyword helper - Payee(address(mkr), wallets.addr("JULIACHANG"), 1.25 ether), // Note: ether is only a keyword helper - Payee(address(mkr), wallets.addr("BYTERON"), 1.25 ether), // Note: ether is only a keyword helper - Payee(address(mkr), wallets.addr("ROCKY"), 1.21 ether), // Note: ether is only a keyword helper - Payee(address(usds), wallets.addr("LIQUIDITY_BOOTSTRAPPING"), 10_000_000 * WAD), - Payee(address(sky), wallets.addr("LIQUIDITY_BOOTSTRAPPING"), 320_000_000 * WAD) + Payee[2] memory payees = [ + Payee(address(dai), wallets.addr("AAVE_V3_TREASURY"), 234_089 ether), // Note: ether is only a keyword helper + Payee(address(sky), wallets.addr("EARLY_BIRD_REWARDS"), 27_222_832.80 ether) // Note: ether is only a keyword helper ]; // Fill the total values from exec sheet PaymentAmounts memory expectedTotalDiff = PaymentAmounts({ - dai: 124_745 * WAD, - mkr: 29.46 ether, // Note: ether is only a keyword helper - usds: 10_000_000 * WAD, - sky: 320_000_000 * WAD + dai: 234_089 ether, // Note: ether is only a keyword helper + mkr: 0 ether, // Note: ether is only a keyword helper + usds: 0 ether, // Note: ether is only a keyword helper + sky: 27_222_832.80 ether // Note: ether is only a keyword helper }); // Vote, schedule and warp, but not yet cast (to get correct surplus balance) @@ -572,11 +564,12 @@ contract DssSpellTest is DssSpellTestBase { pot.drip(); // Calculate and save previous balances - PaymentAmounts memory previousTotalBalance = PaymentAmounts({ - dai: vat.sin(address(vow)), - mkr: mkr.balanceOf(address(pauseProxy)), - usds: usds.balanceOf(address(pauseProxy)), - sky: sky.balanceOf(address(pauseProxy)) + uint256 previousSurplusBalance = vat.sin(address(vow)); + PaymentAmounts memory previousTotalSupply = PaymentAmounts({ + dai: int256(dai.totalSupply()), + mkr: int256(mkr.totalSupply()), + usds: int256(usds.totalSupply()), + sky: int256(sky.totalSupply()) }); PaymentAmounts memory calculatedTotalDiff; PaymentAmounts[] memory previousPayeeBalances = new PaymentAmounts[](payees.length); @@ -593,69 +586,82 @@ contract DssSpellTest is DssSpellTestBase { revert('TestPayments/unexpected-payee-token'); } previousPayeeBalances[i] = PaymentAmounts({ - dai: dai.balanceOf(payees[i].addr), - mkr: mkr.balanceOf(payees[i].addr), - usds: usds.balanceOf(payees[i].addr), - sky: sky.balanceOf(payees[i].addr) + dai: int256(dai.balanceOf(payees[i].addr)), + mkr: int256(mkr.balanceOf(payees[i].addr)), + usds: int256(usds.balanceOf(payees[i].addr)), + sky: int256(sky.balanceOf(payees[i].addr)) }); } + // Check calculated vs expected totals + assertEq( + calculatedTotalDiff.dai, + expectedTotalDiff.dai, + "TestPayments/calculated-vs-expected-dai-total-mismatch" + ); + assertEq( + calculatedTotalDiff.usds, + expectedTotalDiff.usds, + "TestPayments/calculated-vs-expected-usds-total-mismatch" + ); + assertEq( + calculatedTotalDiff.mkr, + expectedTotalDiff.mkr, + "TestPayments/calculated-vs-expected-mkr-total-mismatch" + ); + assertEq( + calculatedTotalDiff.sky, + expectedTotalDiff.sky, + "TestPayments/calculated-vs-expected-sky-total-mismatch" + ); + // Cast spell spell.cast(); assertTrue(spell.done(), "TestPayments/spell-not-done"); - // Check no other transfers were made - PaymentAmounts memory actualBalanceDiff = PaymentAmounts({ - dai: vat.sin(address(vow)) - previousTotalBalance.dai, // We expect debt to increase - mkr: previousTotalBalance.mkr - mkr.balanceOf(address(pauseProxy)), - usds: previousTotalBalance.usds - usds.balanceOf(address(pauseProxy)), - sky: sky.balanceOf(address(pauseProxy)) - previousTotalBalance.sky // We expect Sky balance to increase + // Check calculated vs actual totals + PaymentAmounts memory actualTotalDiff = PaymentAmounts({ + dai: int256(dai.totalSupply()) - previousTotalSupply.dai, + mkr: int256(mkr.totalSupply()) - previousTotalSupply.mkr, + usds: int256(usds.totalSupply()) - previousTotalSupply.usds, + sky: int256(sky.totalSupply()) - previousTotalSupply.sky }); assertEq( - actualBalanceDiff.dai, - (calculatedTotalDiff.dai + calculatedTotalDiff.usds) * RAY, - "TestPayments/vat-sin-mismatch-calculated" + actualTotalDiff.dai, + calculatedTotalDiff.dai + calculatedTotalDiff.usds, + "TestPayments/invalid-dai-sky-total" ); assertEq( - actualBalanceDiff.dai, - (expectedTotalDiff.dai + expectedTotalDiff.usds) * RAY, - "TestPayments/vat-sin-mismatch-expected" - ); - assertLe( - actualBalanceDiff.mkr - (calculatedTotalDiff.mkr + calculatedTotalDiff.sky / afterSpell.sky_mkr_rate) - actualBalanceDiff.sky / afterSpell.sky_mkr_rate, - 1, // To account for rounding errors when converting Sky back to Mkr - "TestPayments/invalid-total" - ); - assertLe( - actualBalanceDiff.mkr - (expectedTotalDiff.mkr + expectedTotalDiff.sky / afterSpell.sky_mkr_rate) - actualBalanceDiff.sky / afterSpell.sky_mkr_rate, - 1, // To account for rounding errors when converting Sky back to Mkr - "TestPayments/invalid-total" + actualTotalDiff.mkr * int256(afterSpell.sky_mkr_rate) + actualTotalDiff.sky, + calculatedTotalDiff.mkr * int256(afterSpell.sky_mkr_rate) + calculatedTotalDiff.sky, + "TestPayments/invalid-mkr-sky-total" ); - assertEq(actualBalanceDiff.usds, 0, "TestPayments/unexpected-usds-balance-change"); + // Check that dai/usds transfers modify surplus buffer + assertEq(vat.sin(address(vow)) - previousSurplusBalance, uint256(calculatedTotalDiff.dai + calculatedTotalDiff.usds) * RAY); // Check that payees received their payments for (uint256 i = 0; i < payees.length; i++) { if (payees[i].token == address(dai)) { assertEq( - dai.balanceOf(payees[i].addr), + int256(dai.balanceOf(payees[i].addr)), previousPayeeBalances[i].dai + payees[i].amount, "TestPayments/invalid-payee-dai-balance" ); } else if (payees[i].token == address(mkr)) { assertEq( - mkr.balanceOf(payees[i].addr), + int256(mkr.balanceOf(payees[i].addr)), previousPayeeBalances[i].mkr + payees[i].amount, "TestPayments/invalid-payee-mkr-balance" ); } else if (payees[i].token == address(usds)) { assertEq( - usds.balanceOf(payees[i].addr), + int256(usds.balanceOf(payees[i].addr)), previousPayeeBalances[i].usds + payees[i].amount, "TestPayments/invalid-payee-usds-balance" ); } else if (payees[i].token == address(sky)) { assertEq( - sky.balanceOf(payees[i].addr), + int256(sky.balanceOf(payees[i].addr)), previousPayeeBalances[i].sky + payees[i].amount, "TestPayments/invalid-payee-sky-balance" ); @@ -873,11 +879,11 @@ contract DssSpellTest is DssSpellTestBase { assertEq(Art, 0, "GUSD-A Art is not 0"); } - function testDaoResolutions() public skipped { // add the `skipped` modifier to skip + function testDaoResolutions() public { // add the `skipped` modifier to skip // For each resolution, add IPFS hash as item to the resolutions array // Initialize the array with the number of resolutions string[1] memory resolutions = [ - "QmaYKt61v6aCTNTYjuHm1Wjpe6JWBzCW2ZHR4XDEJhjm1R" + "QmYJUvw5xbAJmJknG2xUKDLe424JSTWQQhbJCnucRRjUv7" ]; string memory comma_separated_resolutions = ""; @@ -911,4 +917,19 @@ contract DssSpellTest is DssSpellTestBase { } // SPELL-SPECIFIC TESTS GO BELOW + + RwaLiquidationOracleLike oracle = RwaLiquidationOracleLike(addr.addr("MIP21_LIQUIDATION_ORACLE")); + + function testTellAndCullRWA014() public { + bytes32 ilk = "RWA014-A"; + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done()); + + (, , uint tau, uint toc) = oracle.ilks(ilk); + assertGt(toc, 0, _concat("TestError/bad-toc-after-tell-", ilk)); + skip(tau); + assertFalse(oracle.good(ilk), _concat("TestError/still-good-after-tell-", ilk)); + } } diff --git a/src/test/addresses_wallets.sol b/src/test/addresses_wallets.sol index 41220ce1b..8d0ce71bb 100644 --- a/src/test/addresses_wallets.sol +++ b/src/test/addresses_wallets.sol @@ -150,6 +150,9 @@ contract Wallets { // Sky Ecosystem Liquidity Bootstrapping addr["LIQUIDITY_BOOTSTRAPPING"] = 0xD8507ef0A59f37d15B5D7b630FA6EEa40CE4AFdD; + // Early Bird Rewards Multisig + addr["EARLY_BIRD_REWARDS"] = 0x14D98650d46BF7679BBD05D4f615A1547C87Bf68; + // Vest Managers addr["PULLUP_LABS_VEST_MGR"] = 0x9B6213D350A4AFbda2361b6572A07C90c22002F1; diff --git a/src/test/config.sol b/src/test/config.sol index 5f3d29dde..b296bf216 100644 --- a/src/test/config.sol +++ b/src/test/config.sol @@ -305,7 +305,7 @@ contract Config { line: 0, dust: 7_500, pct: 9_25, - mat: 14500, + mat: 15000, liqType: "clip", liqOn: true, chop: 0, @@ -330,7 +330,7 @@ contract Config { line: 0, dust: 25 * THOUSAND, pct: 9_75, - mat: 13000, + mat: 15000, liqType: "clip", liqOn: true, chop: 0, From 270e94e54ca8dbde63ecdf5cf5626277783eb340 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Mon, 14 Oct 2024 09:58:39 +0200 Subject: [PATCH 04/44] improve rwa tests --- src/DssSpell.sol | 6 ++++++ src/DssSpell.t.sol | 28 ++++++++++++++++++++-------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index 66b1e4115..f0abc1bd8 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -351,6 +351,9 @@ contract DssSpellAction is DssAction { // Write-off the debt of RWA007-A and set its oracle price to 0 by calling `cull()` RwaLiquidationOracleLike(MIP21_LIQUIDATION_ORACLE).cull("RWA007-A", RWA007_A_URN); + // Note: update the spot value in vat by propagating the price + DssExecLib.updateCollateralPrice("RWA007-A"); + // Note: in order to decrease global debt ceiling, we need to fetch current `line` (,,,line,) = VatAbstract(MCD_VAT).ilks("RWA014-A"); globalLineReduction += line; @@ -364,6 +367,9 @@ contract DssSpellAction is DssAction { // Write-off the debt of RWA014-A and set its oracle price to 0 by calling `cull()` RwaLiquidationOracleLike(MIP21_LIQUIDATION_ORACLE).cull("RWA014-A", RWA014_A_URN); + // Note: update the spot value in vat by propagating the price + DssExecLib.updateCollateralPrice("RWA014-A"); + // Note: decrease global line VatAbstract(MCD_VAT).file("Line", VatAbstract(MCD_VAT).Line() - globalLineReduction); diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index 91145219f..31cc4eb63 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -918,18 +918,30 @@ contract DssSpellTest is DssSpellTestBase { // SPELL-SPECIFIC TESTS GO BELOW - RwaLiquidationOracleLike oracle = RwaLiquidationOracleLike(addr.addr("MIP21_LIQUIDATION_ORACLE")); - - function testTellAndCullRWA014() public { - bytes32 ilk = "RWA014-A"; + function testRwaTellAndCull() public { + bytes32[2] memory ilks = [ + bytes32("RWA007-A"), + bytes32("RWA014-A") + ]; + RwaLiquidationOracleLike oracle = RwaLiquidationOracleLike(addr.addr("MIP21_LIQUIDATION_ORACLE")); _vote(address(spell)); _scheduleWaitAndCast(address(spell)); assertTrue(spell.done()); - (, , uint tau, uint toc) = oracle.ilks(ilk); - assertGt(toc, 0, _concat("TestError/bad-toc-after-tell-", ilk)); - skip(tau); - assertFalse(oracle.good(ilk), _concat("TestError/still-good-after-tell-", ilk)); + for (uint256 i = 0; i < ilks.length; i++) { + bytes32 ilk = ilks[i]; + (, address pip, uint256 tau, uint256 toc) = oracle.ilks(ilk); + assertGt(toc, 0, _concat("TestError/bad-toc-after-tell-", ilk)); + assertEq(tau, 0, _concat("TestError/bad-tau-after-tell-", ilk)); + assertFalse(oracle.good(ilk), _concat("TestError/still-good-after-tell-", ilk)); + + uint256 price = uint256(DSValueAbstract(pip).read()); + assertEq(price, 0, _concat("TestError/non-zero-oracle-price-after-cull-", ilk)); + + (uint256 Art,, uint256 spot,,) = vat.ilks(ilk); + assertEq(Art, 0, _concat("TestError/non-zero-total-debt-after-cull-", ilk)); + assertEq(spot, 0, _concat("TestError/non-zero-spot-price-after-cull-", ilk)); + } } } From 00d15edc22adadd46115317d8dcf6ad5b345d86f Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Mon, 14 Oct 2024 10:24:33 +0200 Subject: [PATCH 05/44] enable testRemoveChainlogValues --- src/DssSpell.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index 31cc4eb63..854000c51 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -215,9 +215,9 @@ contract DssSpellTest is DssSpellTestBase { //assertEq(OsmAbstract(0xF15993A5C5BE496b8e1c9657Fd2233b579Cd3Bc6).wards(ORACLE_WALLET01), 1); } - function testRemoveChainlogValues() public skipped { // add the `skipped` modifier to skip + function testRemoveChainlogValues() public { // add the `skipped` modifier to skip string[1] memory removedKeys = [ - "FLAPPER_MOM" + "VOTE_DELEGATE_PROXY_FACTORY" ]; for (uint256 i = 0; i < removedKeys.length; i++) { From 7c8ff4ddce77e8ff8a08486790af7e3821ee0cc4 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Mon, 14 Oct 2024 10:33:01 +0200 Subject: [PATCH 06/44] update Flapper instruction to match the sheet --- src/DssSpell.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index f0abc1bd8..ce6756197 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -178,7 +178,7 @@ contract DssSpellAction is DssAction { // Set Flapper farm with bytes32 prevChainlogKey: bytes32(0) prevChainlogKey: bytes32(0), - // Set Flapper farm with bytes32 chainlogKey: REWARDS_LSMKR_USDS + // Set Flapper farm with chainlogKey: "REWARDS_LSMKR_USDS" chainlogKey: "REWARDS_LSMKR_USDS" }) ); From f9e9b957096dd9523ad23286db5f9c229c16423a Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Mon, 14 Oct 2024 11:02:53 +0200 Subject: [PATCH 07/44] enable testOSMs --- src/DssSpell.t.sol | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index 854000c51..d35cb8f0c 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -344,17 +344,26 @@ contract DssSpellTest is DssSpellTestBase { } } - function testOSMs() public skipped { // add the `skipped` modifier to skip - address READER = address(0); + function testOSMs() public { // add the `skipped` modifier to skip + address OSM = addr.addr("PIP_MKR"); + address[4] memory newReaders = [ + addr.addr("MCD_SPOT"), + addr.addr("LOCKSTAKE_CLIP"), + addr.addr("CLIPPER_MOM"), + addr.addr("MCD_END") + ]; - // Track OSM authorizations here - assertEq(OsmAbstract(addr.addr("PIP_TOKEN")).bud(READER), 0); + for (uint256 i = 0; i < newReaders.length; i++) { + assertEq(OsmAbstract(OSM).bud(newReaders[i]), 0); + } _vote(address(spell)); _scheduleWaitAndCast(address(spell)); assertTrue(spell.done(), "TestError/spell-not-done"); - assertEq(OsmAbstract(addr.addr("PIP_TOKEN")).bud(READER), 1); + for (uint256 i = 0; i < newReaders.length; i++) { + assertEq(OsmAbstract(OSM).bud(newReaders[i]), 1); + } } function testMedianizers() public skipped { // add the `skipped` modifier to skip From 0e29a004a3e260dfdf26e1e5e306d0c5a5f89e48 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Mon, 14 Oct 2024 11:21:59 +0200 Subject: [PATCH 08/44] add testNewAuthorizations --- src/DssSpell.t.sol | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index d35cb8f0c..692acab40 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -366,6 +366,41 @@ contract DssSpellTest is DssSpellTestBase { } } + struct Authorization { + bytes32 base; + bytes32 ward; + } + + function testNewAuthorizations() public { // add the `skipped` modifier to skip + Authorization[9] memory newAuthorizations = [ + Authorization({ base: "MCD_VAT", ward: "LOCKSTAKE_ENGINE" }), + Authorization({ base: "MCD_VAT", ward: "LOCKSTAKE_CLIP" }), + Authorization({ base: "PIP_MKR", ward: "OSM_MOM" }), + Authorization({ base: "MCD_DOG", ward: "LOCKSTAKE_CLIP" }), + Authorization({ base: "LOCKSTAKE_MKR", ward: "LOCKSTAKE_ENGINE" }), + Authorization({ base: "LOCKSTAKE_ENGINE", ward: "LOCKSTAKE_CLIP" }), + Authorization({ base: "LOCKSTAKE_CLIP", ward: "MCD_DOG" }), + Authorization({ base: "LOCKSTAKE_CLIP", ward: "MCD_END" }), + Authorization({ base: "LOCKSTAKE_CLIP", ward: "CLIPPER_MOM" }) + ]; + + for (uint256 i = 0; i < newAuthorizations.length; i++) { + address base = addr.addr(newAuthorizations[i].base); + address ward = addr.addr(newAuthorizations[i].ward); + assertEq(WardsAbstract(base).wards(ward), 0, "testNewAuthorizations/already-authorized"); + } + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + for (uint256 i = 0; i < newAuthorizations.length; i++) { + address base = addr.addr(newAuthorizations[i].base); + address ward = addr.addr(newAuthorizations[i].ward); + assertEq(WardsAbstract(base).wards(ward), 1, "testNewAuthorizations/not-authorized"); + } + } + function testMedianizers() public skipped { // add the `skipped` modifier to skip _vote(address(spell)); _scheduleWaitAndCast(address(spell)); From db4bf00780ea7a1bd3bdcd4a7978574f901bdf67 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Mon, 14 Oct 2024 11:28:16 +0200 Subject: [PATCH 09/44] improve global line reduction --- src/DssSpell.sol | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index ce6756197..0406c3987 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -331,16 +331,11 @@ contract DssSpellAction is DssAction { // ---------- Lower Deprecated RWA Debt Ceilings ---------- - // Note: we need extra variables to calculate the decrease of the global debt ceiling - uint256 line; - uint256 globalLineReduction = 0; - // Remove RWA007-A from Debt Ceiling Instant Access Module DssExecLib.removeIlkFromAutoLine("RWA007-A"); // Note: in order to decrease global debt ceiling, we need to fetch current `line` - (,,,line,) = VatAbstract(MCD_VAT).ilks("RWA007-A"); - globalLineReduction += line; + (,,, uint256 line1,) = VatAbstract(MCD_VAT).ilks("RWA007-A"); // Set RWA007-A Debt Ceiling to 0 DssExecLib.setIlkDebtCeiling("RWA007-A", 0); @@ -355,8 +350,7 @@ contract DssSpellAction is DssAction { DssExecLib.updateCollateralPrice("RWA007-A"); // Note: in order to decrease global debt ceiling, we need to fetch current `line` - (,,,line,) = VatAbstract(MCD_VAT).ilks("RWA014-A"); - globalLineReduction += line; + (,,, uint256 line2,) = VatAbstract(MCD_VAT).ilks("RWA014-A"); // Reduce RWA014-A Debt Ceiling by 1.5 billion Dai from 1.5 billion Dai to 0 DssExecLib.setIlkDebtCeiling("RWA014-A", 0); @@ -371,7 +365,7 @@ contract DssSpellAction is DssAction { DssExecLib.updateCollateralPrice("RWA014-A"); // Note: decrease global line - VatAbstract(MCD_VAT).file("Line", VatAbstract(MCD_VAT).Line() - globalLineReduction); + VatAbstract(MCD_VAT).file("Line", VatAbstract(MCD_VAT).Line() - (line1 + line2)); // ---------- Pinwheel DAO Resolution ---------- From ec8b8212e9ca56a7ebe1475013734ab11e150464 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Mon, 14 Oct 2024 11:48:51 +0200 Subject: [PATCH 10/44] test values related to split.farm --- src/DssSpell.sol | 1 - src/DssSpell.t.base.sol | 15 +++++++++++++++ src/test/config.sol | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index 0406c3987..a1ac49f2a 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -188,7 +188,6 @@ contract DssSpellAction is DssAction { // MCD_SPLIT will be set as "rewardsDistribution" in LsMkrUsdsFarm // Provided "hop" will be set as "rewardsDuration" in LsMkrUsdsFarm // New chainlog key REWARDS_LSMKR_USDS will be added - // Provided "hop" will be set as "rewardsDuration" in LsMkrUsdsFarm // Note: above instructions are taken inside FlapperInit.setFarm method // Note: prepare "farms" variable used inside Lockstake init call below diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 281ddc8a4..60bfc0eb6 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -228,6 +228,11 @@ interface LitePsmMomLike is AuthorityLike { function halt(address, uint8) external; } +interface StakingRewardsLike { + function rewardsDistribution() external view returns (address); + function rewardsDuration() external view returns (uint256); +} + contract DssSpellTestBase is Config, DssTest { using stdStorage for StdStorage; @@ -622,6 +627,16 @@ contract DssSpellTestBase is Config, DssTest { uint256 normalizedTestBurn = values.split_burn * 10**14; assertEq(split.burn(), normalizedTestBurn, "TestError/split-burn"); assertTrue(split.burn() >= 50 * WAD / 100 && split.burn() <= 1 * WAD, "TestError/split-burn-range"); // gte 50% and lte 100% + // check farm address + address split_farm = addr.addr(values.split_farm); + assertEq(split.farm(), split_farm, "TestError/split-farm"); + // check rewards distribution and duration + if (split_farm != address(0)) { + address rewardsDistribution = StakingRewardsLike(split_farm).rewardsDistribution(); + assertEq(rewardsDistribution, address(split), "TestError/farm-distribution"); + uint256 rewardsDuration = StakingRewardsLike(split_farm).rewardsDuration(); + assertEq(rewardsDuration, values.split_hop, "TestError/farm-duration-does-not-match-split-hop"); + } } // flap diff --git a/src/test/config.sol b/src/test/config.sol index b296bf216..889ffb8d4 100644 --- a/src/test/config.sol +++ b/src/test/config.sol @@ -40,6 +40,7 @@ contract Config { uint256 vow_hump_max; uint256 split_hop; uint256 split_burn; + bytes32 split_farm; uint256 flap_want; uint256 dog_Hole; uint256 esm_min; @@ -126,6 +127,7 @@ contract Config { afterSpell.vow_hump_max = 60 * MILLION; // In whole Dai units afterSpell.split_hop = 15_649 seconds; // In seconds afterSpell.split_burn = 70_00; // In basis points + afterSpell.split_farm = "REWARDS_LSMKR_USDS"; // Farm chainlog key afterSpell.flap_want = 9800; // In basis points afterSpell.dog_Hole = 150 * MILLION; // In whole Dai units afterSpell.esm_min = 300 * THOUSAND; // In whole MKR units From 57c1a1a27aecf00567354e3fa61eadc7b6b847f2 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Mon, 14 Oct 2024 14:20:55 +0200 Subject: [PATCH 11/44] update registry name and symbol --- src/DssSpell.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index a1ac49f2a..81e57b56c 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -299,11 +299,11 @@ contract DssSpellAction is DssAction { // Init Lockstake Engine with uint256 tolerance: 0.5 tolerance: 5 * RAY / 10, - // Init Lockstake Engine with string name: "LOCKSTAKE" - name: "LOCKSTAKE", + // Init Lockstake Engine with string name: "LockstakeMkr" + name: "LockstakeMkr", - // Init Lockstake Engine with string symbol: "LMKR" - symbol: "LMKR" + // Init Lockstake Engine with string symbol: "lsMKR" + symbol: "lsMKR" }) ); From f7ea69f08b6233163d9a38a58f6fe53f2dd66b10 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Mon, 14 Oct 2024 16:47:01 +0200 Subject: [PATCH 12/44] test engine_fee and engine_farms via base tests --- src/DssSpell.t.base.sol | 41 ++++++++++++ src/test/config.sol | 139 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 60bfc0eb6..f6d6cb227 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -233,6 +233,36 @@ interface StakingRewardsLike { function rewardsDuration() external view returns (uint256); } +interface LockstakeEngineLike { + function voteDelegateFactory() external view returns (address); + function vat() external view returns (address); + function usdsJoin() external view returns (address); + function usds() external view returns (address); + function ilk() external view returns (bytes32); + function mkr() external view returns (address); + function lsmkr() external view returns (address); + function fee() external view returns (uint256); + function mkrSky() external view returns (address); + function sky() external view returns (address); + function rely(address) external; + function file(bytes32, address) external; + function file(bytes32, uint256) external; + function addFarm(address) external; + function farms(address farm) external view returns (uint8); +} + +interface LockstakeClipperLike { + function vat() external view returns (address); + function dog() external view returns (address); + function spotter() external view returns (address); + function engine() external view returns (address); + function ilk() external view returns (bytes32); + function rely(address) external; + function file(bytes32, address) external; + function file(bytes32, uint256) external; + function upchost() external; +} + contract DssSpellTestBase is Config, DssTest { using stdStorage for StdStorage; @@ -809,6 +839,17 @@ contract DssSpellTestBase is Config, DssTest { uint256 _chost = (values.collaterals[ilk].dust * RAD) * normalizedTestChop / WAD; assertEq(clip.chost(), _chost, _concat("TestError/calc-chost-incorrect-", ilk)); // Ensure clip.upchost() is called when dust changes } + if (reg.class(ilk) == 7) { + address engine = LockstakeClipperLike(address(clip)).engine(); + assertNotEq(engine, address(0), _concat("TestError/engine-is-not-set-", ilk)); + uint256 normalisedFee = values.collaterals[ilk].engine_fee * WAD / 10_000; + assertEq(normalisedFee, LockstakeEngineLike(engine).fee(), _concat("TestError/engine-fee-", ilk)); + for (uint256 j = 0; j < values.collaterals[ilk].engine_farms.length; j++) { + address farm = addr.addr(values.collaterals[ilk].engine_farms[j]); + assertNotEq(farm, address(0), _concat("TestError/farm-is-empty-", ilk)); + assertEq(LockstakeEngineLike(engine).farms(farm), 1, _concat("TestError/engine-farms-", ilk)); + } + } } if (reg.class(ilk) < 3) { { diff --git a/src/test/config.sol b/src/test/config.sol index 889ffb8d4..5a2f697e8 100644 --- a/src/test/config.sol +++ b/src/test/config.sol @@ -83,6 +83,8 @@ contract Config { uint256 calc_tau; uint256 calc_step; uint256 calc_cut; + uint256 engine_fee; + bytes32[] engine_farms; bool offboarding; } @@ -172,6 +174,8 @@ contract Config { calc_tau: 0, // In seconds calc_step: 90, // In seconds calc_cut: 9900, // In basis points + engine_fee: 0, // In basis points + engine_farms: new bytes32[](0), // Array of chainlog keys of added farms offboarding: false // If mat is being offboarded }); afterSpell.collaterals["ETH-B"] = CollateralValues({ @@ -197,6 +201,8 @@ contract Config { calc_tau: 0, calc_step: 60, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["ETH-C"] = CollateralValues({ @@ -222,6 +228,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["BAT-A"] = CollateralValues({ @@ -247,6 +255,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["USDC-A"] = CollateralValues({ @@ -272,6 +282,8 @@ contract Config { calc_tau: 4_320_000, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["USDC-B"] = CollateralValues({ @@ -297,6 +309,8 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["WBTC-A"] = CollateralValues({ @@ -322,6 +336,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["WBTC-B"] = CollateralValues({ @@ -347,6 +363,8 @@ contract Config { calc_tau: 0, calc_step: 60, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["WBTC-C"] = CollateralValues({ @@ -372,6 +390,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["TUSD-A"] = CollateralValues({ @@ -397,6 +417,8 @@ contract Config { calc_tau: 250 days, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["KNC-A"] = CollateralValues({ @@ -422,6 +444,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["ZRX-A"] = CollateralValues({ @@ -447,6 +471,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["MANA-A"] = CollateralValues({ @@ -472,6 +498,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["USDT-A"] = CollateralValues({ @@ -497,6 +525,8 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["PAXUSD-A"] = CollateralValues({ @@ -522,6 +552,8 @@ contract Config { calc_tau: 4_320_000, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["COMP-A"] = CollateralValues({ @@ -547,6 +579,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["LRC-A"] = CollateralValues({ @@ -572,6 +606,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["LINK-A"] = CollateralValues({ @@ -597,6 +633,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["BAL-A"] = CollateralValues({ @@ -622,6 +660,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["YFI-A"] = CollateralValues({ @@ -647,6 +687,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["GUSD-A"] = CollateralValues({ @@ -672,6 +714,8 @@ contract Config { calc_tau: 4_320_000, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNI-A"] = CollateralValues({ @@ -697,6 +741,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["RENBTC-A"] = CollateralValues({ @@ -722,6 +768,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["AAVE-A"] = CollateralValues({ @@ -747,6 +795,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNIV2DAIETH-A"] = CollateralValues({ @@ -772,6 +822,8 @@ contract Config { calc_tau: 0, calc_step: 125, calc_cut: 9950, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["PSM-USDC-A"] = CollateralValues({ @@ -797,6 +849,8 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["LITE-PSM-USDC-A"] = CollateralValues({ @@ -822,6 +876,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["UNIV2WBTCETH-A"] = CollateralValues({ @@ -847,6 +903,8 @@ contract Config { calc_tau: 0, calc_step: 130, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNIV2USDCETH-A"] = CollateralValues({ @@ -872,6 +930,8 @@ contract Config { calc_tau: 0, calc_step: 125, calc_cut: 9950, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNIV2DAIUSDC-A"] = CollateralValues({ @@ -897,6 +957,8 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["UNIV2ETHUSDT-A"] = CollateralValues({ @@ -922,6 +984,8 @@ contract Config { calc_tau: 0, calc_step: 125, calc_cut: 9950, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["UNIV2LINKETH-A"] = CollateralValues({ @@ -947,6 +1011,8 @@ contract Config { calc_tau: 0, calc_step: 130, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNIV2UNIETH-A"] = CollateralValues({ @@ -972,6 +1038,8 @@ contract Config { calc_tau: 0, calc_step: 130, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["UNIV2WBTCDAI-A"] = CollateralValues({ @@ -997,6 +1065,8 @@ contract Config { calc_tau: 0, calc_step: 125, calc_cut: 9950, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNIV2AAVEETH-A"] = CollateralValues({ @@ -1022,6 +1092,8 @@ contract Config { calc_tau: 0, calc_step: 130, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNIV2DAIUSDT-A"] = CollateralValues({ @@ -1047,6 +1119,8 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA001-A"] = CollateralValues({ @@ -1072,6 +1146,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA002-A"] = CollateralValues({ @@ -1097,6 +1173,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA003-A"] = CollateralValues({ @@ -1122,6 +1200,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA004-A"] = CollateralValues({ @@ -1147,6 +1227,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA005-A"] = CollateralValues({ @@ -1172,6 +1254,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA006-A"] = CollateralValues({ @@ -1197,6 +1281,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA007-A"] = CollateralValues({ @@ -1222,6 +1308,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA008-A"] = CollateralValues({ @@ -1247,6 +1335,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA009-A"] = CollateralValues({ @@ -1272,6 +1362,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA010-A"] = CollateralValues({ @@ -1297,6 +1389,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA011-A"] = CollateralValues({ @@ -1322,6 +1416,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA012-A"] = CollateralValues({ @@ -1347,6 +1443,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA013-A"] = CollateralValues({ @@ -1372,6 +1470,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA014-A"] = CollateralValues({ @@ -1397,6 +1497,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA015-A"] = CollateralValues({ @@ -1422,6 +1524,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["MATIC-A"] = CollateralValues({ @@ -1447,6 +1551,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["PSM-PAX-A"] = CollateralValues({ @@ -1472,6 +1578,8 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["GUNIV3DAIUSDC1-A"] = CollateralValues({ @@ -1497,6 +1605,8 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["WSTETH-A"] = CollateralValues({ @@ -1522,6 +1632,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["WSTETH-B"] = CollateralValues({ @@ -1547,6 +1659,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["DIRECT-SPK-AAVE-LIDO-USDS"] = CollateralValues({ @@ -1572,6 +1686,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["DIRECT-AAVEV2-DAI"] = CollateralValues({ @@ -1597,6 +1713,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["DIRECT-COMPV2-DAI"] = CollateralValues({ @@ -1622,6 +1740,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["PSM-GUSD-A"] = CollateralValues({ @@ -1647,6 +1767,8 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["GUNIV3DAIUSDC2-A"] = CollateralValues({ @@ -1672,6 +1794,8 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["CRVV1ETHSTETH-A"] = CollateralValues({ @@ -1697,6 +1821,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["TELEPORT-FW-A"] = CollateralValues({ @@ -1722,6 +1848,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RETH-A"] = CollateralValues({ @@ -1747,6 +1875,8 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 99_00, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["GNO-A"] = CollateralValues({ @@ -1772,6 +1902,8 @@ contract Config { calc_tau: 0, calc_step: 60, calc_cut: 99_00, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["DIRECT-SPARK-DAI"] = CollateralValues({ @@ -1797,6 +1929,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["DIRECT-SPARK-MORPHO-DAI"] = CollateralValues({ @@ -1822,6 +1956,8 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, + engine_fee: 0, + engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["LSE-MKR-A"] = CollateralValues({ @@ -1847,7 +1983,10 @@ contract Config { calc_tau: 0, calc_step: 60, calc_cut: 99_00, + engine_fee: 5_00, + engine_farms: new bytes32[](1), offboarding: false }); + afterSpell.collaterals["LSE-MKR-A"].engine_farms[0] = "REWARDS_LSMKR_USDS"; } } From 66a805344e026f3c561349df52b5ff81a442f098 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Mon, 14 Oct 2024 19:02:05 +0200 Subject: [PATCH 13/44] add new spark spell --- src/DssSpell.sol | 7 +++---- src/DssSpell.t.sol | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index 81e57b56c..332f93150 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -106,7 +106,7 @@ contract DssSpellAction is DssAction { // ---------- Spark Proxy Spell ---------- // Spark Proxy: https://github.com/marsfoundation/sparklend-deployments/blob/bba4c57d54deb6a14490b897c12a949aa035a99b/script/output/1/primary-sce-latest.json#L2 address internal constant SPARK_PROXY = 0x3300f198988e4C9C63F75dF86De36421f06af8c4; - address internal constant SPARK_SPELL = 0x74b3D0E74f2711f30442536832D7fBCB0F42C195; + address internal constant SPARK_SPELL = 0xcc3B9e79261A7064A0f734Cc749A8e3762e0a187; function actions() public override { // Note: multple actions in the spell depend on DssInstance @@ -378,9 +378,8 @@ contract DssSpellAction is DssAction { // ---------- Spark Spell ---------- - // Execute Spark Proxy Spell at 0x74b3D0E74f2711f30442536832D7fBCB0F42C195 - // ProxyLike(SPARK_PROXY).exec(SPARK_SPELL, abi.encodeWithSignature("execute()")); - // TODO: update spell address when redeployed + // Execute Spark Proxy Spell at 0xcc3B9e79261A7064A0f734Cc749A8e3762e0a187 + ProxyLike(SPARK_PROXY).exec(SPARK_SPELL, abi.encodeWithSignature("execute()")); // ---------- Chainlog bump ---------- diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index 692acab40..773e9e485 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -942,9 +942,9 @@ contract DssSpellTest is DssSpellTestBase { } // SPARK TESTS - function testSparkSpellIsExecuted() public skipped { // add the `skipped` modifier to skip + function testSparkSpellIsExecuted() public { // add the `skipped` modifier to skip address SPARK_PROXY = addr.addr('SPARK_PROXY'); - address SPARK_SPELL = 0xc80621140bEe6A105C180Ae7cb0a084c2409C738; + address SPARK_SPELL = 0xcc3B9e79261A7064A0f734Cc749A8e3762e0a187; vm.expectCall( SPARK_PROXY, From 94d6a0f8461c93e39de61b11410d3e6df4f1bf5f Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Mon, 14 Oct 2024 19:06:47 +0200 Subject: [PATCH 14/44] update instructions --- src/DssSpell.sol | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index 332f93150..159c10edc 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -114,29 +114,30 @@ contract DssSpellAction is DssAction { // ---------- Setup new MkrOsm ---------- - // Authorize MkrOsm at 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b to read MKR oracle price from PIP_MKR using the following parameters: - // Authorize MkrOSm with address _oracle: PIP_MKR from chainlog - // Authorize MkrOSm with address _reader: 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b + // Whitelist MkrOsm to read from current PIP_MKR using `DssExecLib.addReaderToWhitelist` with the following parameters: + // Set parameter address _oracle: PIP_MKR address from chainlog (0xdbbe5e9b1daa91430cf0772fcebe53f6c6f137df) + // Set parameter address _reader: 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b DssExecLib.addReaderToWhitelist(PIP_MKR, NEW_PIP_MKR); - // Set MkrOsm at 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b as "PIP_MKR" in the chainlog using the following parameters: - // Set MkrOsm with bytes32 _key: "PIP_MKR" - // Set MkrOsm with address _val: 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b + // Set MkrOsm as "PIP_MKR" in the chainlog using the following parameters: + // Set parameter bytes32 _key: "PIP_MKR" + // Set parameter address _val: 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b DssExecLib.setChangelogAddress("PIP_MKR", NEW_PIP_MKR); // ---------- Setup new VoteDelegateFactory ---------- + // Rename "VOTE_DELEGATE_PROXY_FACTORY" to "VOTE_DELEGATE_FACTORY_LEGACY" in chainlog: + // Note: this is a subheading, actual instructions are below - // Rename "VOTE_DELEGATE_PROXY_FACTORY" to "VOTE_DELEGATE_FACTORY_LEGACY" in chainlog with the following parameters: - // Rename chainlog item address with bytes32 _key: "VOTE_DELEGATE_PROXY_FACTORY" - dss.chainlog.removeAddress("VOTE_DELEGATE_PROXY_FACTORY"); - - // Rename chainlog item with bytes32 _key: "VOTE_DELEGATE_FACTORY_LEGACY" - // Rename chainlog item with address _val (VOTE_DELEGATE_PROXY_FACTORY from chainlog) + // Call DssExecLib.setChangelogAddress with the following parameters: + // Set parameter bytes32 _key: "VOTE_DELEGATE_FACTORY_LEGACY" + // Set parameter address _val: VOTE_DELEGATE_PROXY_FACTORY address (0xd897f108670903d1d6070fcf818f9db3615af272) from the chainlog DssExecLib.setChangelogAddress("VOTE_DELEGATE_FACTORY_LEGACY", VOTE_DELEGATE_PROXY_FACTORY); - // Set "VOTE_DELEGATE_FACTORY" in the chainlog to 0xC3D809E87A2C9da4F6d98fECea9135d834d6F5A0 with the following parameters: - // Set new chainlog item with bytes32 _key: "VOTE_DELEGATE_FACTORY" - // Set new chainlog item with address _val: 0xC3D809E87A2C9da4F6d98fECea9135d834d6F5A0 + // Call CHAINLOG.removeAddress with the following parameters: + // Set parameter bytes32 _key: "VOTE_DELEGATE_PROXY_FACTORY" + dss.chainlog.removeAddress("VOTE_DELEGATE_PROXY_FACTORY"); + + // Set "VOTE_DELEGATE_FACTORY" in the chainlog to 0xC3D809E87A2C9da4F6d98fECea9135d834d6F5A0 DssExecLib.setChangelogAddress("VOTE_DELEGATE_FACTORY", VOTE_DELEGATE_FACTORY); // ---------- Setup Lockstake Engine ---------- From 5699e49aa93891b599761730048d983b12b35453 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Mon, 14 Oct 2024 19:20:44 +0200 Subject: [PATCH 15/44] add reasoning and authority urls --- src/DssSpell.sol | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index 159c10edc..bf2ff9647 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -113,6 +113,8 @@ contract DssSpellAction is DssAction { DssInstance memory dss = MCD.loadFromChainlog(DssExecLib.LOG); // ---------- Setup new MkrOsm ---------- + // Forum: https://forum.sky.money/t/atlas-weekly-cycle-edit-proposal-week-of-october-14-2024-01/25324 + // Poll: https://vote.makerdao.com/polling/QmUm8Krq // Whitelist MkrOsm to read from current PIP_MKR using `DssExecLib.addReaderToWhitelist` with the following parameters: // Set parameter address _oracle: PIP_MKR address from chainlog (0xdbbe5e9b1daa91430cf0772fcebe53f6c6f137df) @@ -125,8 +127,11 @@ contract DssSpellAction is DssAction { DssExecLib.setChangelogAddress("PIP_MKR", NEW_PIP_MKR); // ---------- Setup new VoteDelegateFactory ---------- + // Forum: https://forum.sky.money/t/atlas-weekly-cycle-edit-proposal-week-of-october-14-2024-01/25324 + // Poll: https://vote.makerdao.com/polling/QmUm8Krq + // Rename "VOTE_DELEGATE_PROXY_FACTORY" to "VOTE_DELEGATE_FACTORY_LEGACY" in chainlog: - // Note: this is a subheading, actual instructions are below + // Note: this is a meta instruction, actual instructions are below // Call DssExecLib.setChangelogAddress with the following parameters: // Set parameter bytes32 _key: "VOTE_DELEGATE_FACTORY_LEGACY" @@ -141,6 +146,8 @@ contract DssSpellAction is DssAction { DssExecLib.setChangelogAddress("VOTE_DELEGATE_FACTORY", VOTE_DELEGATE_FACTORY); // ---------- Setup Lockstake Engine ---------- + // Forum: https://forum.sky.money/t/atlas-weekly-cycle-edit-proposal-week-of-october-14-2024-01/25324 + // Poll: https://vote.makerdao.com/polling/QmUm8Krq // SBE Parameter Changes // Note: this is a subheading, actual instructions are below @@ -325,11 +332,14 @@ contract DssSpellAction is DssAction { // Note: above instructions are taken inside FlapperInit.setFarm method // ---------- Fund Early Bird Rewards Multisig ---------- + // Forum: https://forum.sky.money/t/atlas-weekly-cycle-edit-proposal-week-of-october-14-2024-01/25324#p-99402-early-bird-bonus-3 + // Poll: https://vote.makerdao.com/polling/QmUm8Krq // Mint 27,222,832.80 SKY to 0x14D98650d46BF7679BBD05D4f615A1547C87Bf68 SkyLike(SKY).mint(EARLY_BIRD_REWARDS, 27_222_832.80 ether); // Note: ether is only a keyword helper // ---------- Lower Deprecated RWA Debt Ceilings ---------- + // Forum: https://forum.sky.money/t/2024-10-17-expected-executive-contents-rwa-vault-changes/25323 // Remove RWA007-A from Debt Ceiling Instant Access Module DssExecLib.removeIlkFromAutoLine("RWA007-A"); @@ -368,16 +378,22 @@ contract DssSpellAction is DssAction { VatAbstract(MCD_VAT).file("Line", VatAbstract(MCD_VAT).Line() - (line1 + line2)); // ---------- Pinwheel DAO Resolution ---------- + // Forum: https://forum.sky.money/t/coinbase-web3-wallet-legal-overview/24577/3 // Approve DAO Resolution at QmYJUvw5xbAJmJknG2xUKDLe424JSTWQQhbJCnucRRjUv7 // Note: see `dao_resolutions` public variable declared above // ---------- AAVE Revenue Share Payment ---------- + // Forum: https://forum.sky.money/t/spark-aave-revenue-share-calculation-payment-5-q3-2024/25286 // AAVE Revenue Share - 234089 DAI - 0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c DssExecLib.sendPaymentFromSurplusBuffer(AAVE_V3_TREASURY, 234_089); // ---------- Spark Spell ---------- + // Forum: https://forum.sky.money/t/oct-3-2024-proposed-changes-to-spark-for-upcoming-spell/25293 + // Poll: https://vote.makerdao.com/polling/QmbHaA2G + // Poll: https://vote.makerdao.com/polling/QmShWccA + // Poll: https://vote.makerdao.com/polling/QmTksxrr // Execute Spark Proxy Spell at 0xcc3B9e79261A7064A0f734Cc749A8e3762e0a187 ProxyLike(SPARK_PROXY).exec(SPARK_SPELL, abi.encodeWithSignature("execute()")); From 46de779fb02a155b625a30e5ac8efec846a6d546 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Tue, 15 Oct 2024 09:14:15 +0200 Subject: [PATCH 16/44] add exec doc url and hash --- src/DssSpell.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index bf2ff9647..b849cc17a 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -46,9 +46,9 @@ interface ProxyLike { 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 'TODO' -q -O - 2>/dev/null)" + // Hash: cast keccak -- "$(wget 'https://raw.githubusercontent.com/makerdao/community/20b4c0ed4bb2771483a0861747cf34a25080ad21/governance/votes/Executive%20vote%20-%20October%2017%2C%202024.md' -q -O - 2>/dev/null)" string public constant override description = - "2024-10-17 MakerDAO Executive Spell | Hash: TODO"; + "2024-10-17 MakerDAO Executive Spell | Hash: 0xcbbb4fa0c3bce6e8d97c46e0d4a7aba50d42b184a6f58c0f1b1cf2e0da849858"; // Set office hours according to the summary function officeHours() public pure override returns (bool) { From a86811f66979339e83456fcdb0ee719c4aadca26 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Tue, 15 Oct 2024 09:33:47 +0200 Subject: [PATCH 17/44] add comments to tests --- src/DssSpell.t.base.sol | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index f6d6cb227..c14d7ae8c 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -657,10 +657,10 @@ contract DssSpellTestBase is Config, DssTest { uint256 normalizedTestBurn = values.split_burn * 10**14; assertEq(split.burn(), normalizedTestBurn, "TestError/split-burn"); assertTrue(split.burn() >= 50 * WAD / 100 && split.burn() <= 1 * WAD, "TestError/split-burn-range"); // gte 50% and lte 100% - // check farm address + // check split.farm address to match config address split_farm = addr.addr(values.split_farm); assertEq(split.farm(), split_farm, "TestError/split-farm"); - // check rewards distribution and duration + // check farm rewards distribution and duration to match splitter if (split_farm != address(0)) { address rewardsDistribution = StakingRewardsLike(split_farm).rewardsDistribution(); assertEq(rewardsDistribution, address(split), "TestError/farm-distribution"); @@ -840,10 +840,13 @@ contract DssSpellTestBase is Config, DssTest { assertEq(clip.chost(), _chost, _concat("TestError/calc-chost-incorrect-", ilk)); // Ensure clip.upchost() is called when dust changes } if (reg.class(ilk) == 7) { + // check correct clipper type is used for the reg.class 7 address engine = LockstakeClipperLike(address(clip)).engine(); - assertNotEq(engine, address(0), _concat("TestError/engine-is-not-set-", ilk)); + assertNotEq(engine, address(0), _concat("TestError/clip-engine-is-not-set-", ilk)); + // check lockstake engine fee to match config uint256 normalisedFee = values.collaterals[ilk].engine_fee * WAD / 10_000; - assertEq(normalisedFee, LockstakeEngineLike(engine).fee(), _concat("TestError/engine-fee-", ilk)); + assertEq(LockstakeEngineLike(engine).fee(), normalisedFee, _concat("TestError/engine-fee-", ilk)); + // check lockstake engine farms to match config for (uint256 j = 0; j < values.collaterals[ilk].engine_farms.length; j++) { address farm = addr.addr(values.collaterals[ilk].engine_farms[j]); assertNotEq(farm, address(0), _concat("TestError/farm-is-empty-", ilk)); From 5e0a5423c5475078b9d87b3655d93ba0d52d549c Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Tue, 15 Oct 2024 09:40:48 +0200 Subject: [PATCH 18/44] improve testNewAuthorizations error --- src/DssSpell.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index 773e9e485..f223bf61d 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -387,7 +387,7 @@ contract DssSpellTest is DssSpellTestBase { for (uint256 i = 0; i < newAuthorizations.length; i++) { address base = addr.addr(newAuthorizations[i].base); address ward = addr.addr(newAuthorizations[i].ward); - assertEq(WardsAbstract(base).wards(ward), 0, "testNewAuthorizations/already-authorized"); + assertEq(WardsAbstract(base).wards(ward), 0, _concat("testNewAuthorizations/already-authorized-", newAuthorizations[i].base)); } _vote(address(spell)); @@ -397,7 +397,7 @@ contract DssSpellTest is DssSpellTestBase { for (uint256 i = 0; i < newAuthorizations.length; i++) { address base = addr.addr(newAuthorizations[i].base); address ward = addr.addr(newAuthorizations[i].ward); - assertEq(WardsAbstract(base).wards(ward), 1, "testNewAuthorizations/not-authorized"); + assertEq(WardsAbstract(base).wards(ward), 1, _concat("testNewAuthorizations/not-authorized-", newAuthorizations[i].base)); } } From 26c2b165ff5b78123a3da93eea6d45d73b440921 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Tue, 15 Oct 2024 13:49:25 +0200 Subject: [PATCH 19/44] add initial testLockstakeIlkIntegration --- src/DssSpell.t.base.sol | 139 ++++++++++++++++++++++++++++++++++++++++ src/DssSpell.t.sol | 33 +++++----- 2 files changed, 155 insertions(+), 17 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index c14d7ae8c..a78ad500f 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -249,6 +249,9 @@ interface LockstakeEngineLike { function file(bytes32, uint256) external; function addFarm(address) external; function farms(address farm) external view returns (uint8); + function open(uint256 index) external returns (address urn); + function draw(address owner, uint256 index, address to, uint256 wad) external; + function lock(address owner, uint256 index, uint256 wad, uint16 ref) external; } interface LockstakeClipperLike { @@ -261,6 +264,10 @@ interface LockstakeClipperLike { function file(bytes32, address) external; function file(bytes32, uint256) external; function upchost() external; + function sales(uint256) + external + view + returns (uint256 pos, uint256 tab, uint256 lot, uint256 tot, address usr, uint96 tic, uint256 top); } contract DssSpellTestBase is Config, DssTest { @@ -1199,6 +1206,138 @@ contract DssSpellTestBase is Config, DssTest { } } + struct LockstakeIlkParams { + bytes32 ilk; + address clip; + address calc; + address pip; + uint256 ilkAmt; + } + + function _checkLockstakeIlkIntegration( + LockstakeIlkParams memory p + ) internal { + assertEq(vat.dai(address(this)), 0, "LockstakeIlkIntegration/non-zero-initial-dai"); + // Check that all contracts are set + { + assertEq(dog.vat(), address(vat), "LockstakeIlkIntegration/invalid-dog-vat"); + assertEq(dog.vow(), address(vow), "LockstakeIlkIntegration/invalid-dog-vow"); + (address clip,,,) = dog.ilks(p.ilk); + assertEq(clip, p.clip, "LockstakeIlkIntegration/invalid-dog-clip"); + assertEq(ClipAbstract(p.clip).ilk(), p.ilk, "LockstakeIlkIntegration/invalid-clip-ilk"); + assertEq(ClipAbstract(p.clip).vat(), address(vat), "LockstakeIlkIntegration/invalid-clip-vat"); + assertEq(ClipAbstract(p.clip).vow(), address(vow), "LockstakeIlkIntegration/invalid-clip-vow"); + assertEq(ClipAbstract(p.clip).dog(), address(dog), "LockstakeIlkIntegration/invalid-clip-dog"); + assertEq(ClipAbstract(p.clip).spotter(), address(spotter), "LockstakeIlkIntegration/invalid-clip-spotter"); + assertEq(ClipAbstract(p.clip).calc(), p.calc, "LockstakeIlkIntegration/invalid-clip-calc"); + } + // Check all required authorizations + { + assertEq(vat.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-vat-clip"); + assertEq(dog.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-dog-clip"); + assertEq(ClipAbstract(p.clip).wards(address(dog)), 1, "LockstakeIlkIntegration/missing-auth-clip-dog"); + assertEq(ClipAbstract(p.clip).wards(address(end)), 1, "LockstakeIlkIntegration/missing-auth-clip-end"); + assertEq(ClipAbstract(p.clip).wards(address(clipMom)), 1, "LockstakeIlkIntegration/missing-auth--cliclippMom"); + } + // Check OSM buds + { + assertEq(OsmAbstract(p.pip).bud(address(spotter)), 1, "LockstakeIlkIntegration/missing-spotter-bud"); + assertEq(OsmAbstract(p.pip).bud(p.clip), 1, "LockstakeIlkIntegration/missing-clip-bud"); + assertEq(OsmAbstract(p.pip).bud(address(clipMom)), 1, "LockstakeIlkIntegration/missing-clipMom-bud"); + assertEq(OsmAbstract(p.pip).bud(address(end)), 1, "LockstakeIlkIntegration/missing-end-bud"); + } + // Prepare for liquidation + { + // Force max Hole + vm.store( + address(dog), + bytes32(uint256(4)), + bytes32(type(uint256).max) + ); + // Initially this test assume that's we are using freshly deployed Cliiper contract without any past auctions + if (ClipAbstract(p.clip).kicks() > 0) { + vm.store( + p.clip, + bytes32(uint256(10)), + bytes32(uint256(0)) + ); + assertEq(ClipAbstract(p.clip).kicks(), 0, "LockstakeIlkIntegration/unchanged-kicks"); + } + } + // Open new vault + LockstakeEngineLike engine = LockstakeEngineLike(LockstakeClipperLike(p.clip).engine()); + address urn = engine.open(0); + GemAbstract token = GemAbstract(reg.gem(p.ilk)); + // Lock collateral + { + uint256 tknAmt = p.ilkAmt / 10 ** (18 - token.decimals()); + _giveTokens(address(token), tknAmt); + assertEq(token.balanceOf(address(this)), tknAmt, "LockstakeIlkIntegration/unchanged-balance"); + token.approve(address(engine), tknAmt); + engine.lock(address(this), 0, tknAmt, 0); + } + // Simulate normal operation of the OSM + OsmAbstract(p.pip).poke(); + vm.warp(block.timestamp + 1 hours); + OsmAbstract(p.pip).poke(); + // Generate new DAI to force a liquidation + int256 art; + uint256 rate; + { + uint256 line; + uint256 spot; + spotter.poke(p.ilk); + jug.drip(p.ilk); + (, rate, spot, line,) = vat.ilks(p.ilk); + art = int256(p.ilkAmt * spot / rate); + assertGt(art, 0, "LockstakeIlkIntegration/art-is-zero"); + _setIlkLine(p.ilk, type(uint256).max); + engine.draw(address(this), 0, address(this), uint256(art)); + _setIlkLine(p.ilk, line); + } + // Liquidate the vault + address keeper1 = address(111); + { + _setIlkMat(p.ilk, 100000 * RAY); + vm.warp(block.timestamp + 10 days); + spotter.poke(p.ilk); + assertEq(ClipAbstract(p.clip).kicks(), 0, "LockstakeIlkIntegration/unexpected-kicks-before"); + dog.bark(p.ilk, urn, keeper1); + assertEq(ClipAbstract(p.clip).kicks(), 1, "LockstakeIlkIntegration/unexpected-kicks-after"); + + (, rate,,,) = vat.ilks(p.ilk); + uint256 debt = rate * uint256(art) * dog.chop(p.ilk) / WAD; + vm.store( + address(vat), + keccak256(abi.encode(address(this), uint256(5))), + bytes32(debt) + ); + assertEq(vat.gem(p.ilk, p.clip), p.ilkAmt, "LockstakeIlkIntegration/unexpected-clip-gem-amt"); + assertGt(vat.dai(keeper1), 0, "LockstakeIlkIntegration/unexpected-zero-dai-after-bark"); + } + // Take auction + address keeper2 = address(222); + { + vm.warp(block.timestamp + 20 minutes); + (, uint256 tab, uint256 lot,,,, uint256 top) = LockstakeClipperLike(p.clip).sales(1); + + assertEq(lot, p.ilkAmt, "LockstakeIlkIntegration/unexpected-lot"); + assertGt(lot * top, tab, "LockstakeIlkIntegration/not-enough-to-cover-debt"); + + vat.hope(p.clip); + ClipAbstract(p.clip).take(1, lot, top, keeper2, bytes("")); + } + // Check values after take + { + (, uint256 tab, uint256 lot,, address usr,,) = LockstakeClipperLike(p.clip).sales(1); + assertEq(usr, address(0), "LockstakeIlkIntegration/unexpected-usr"); + assertEq(tab, 0, "LockstakeIlkIntegration/non-zero-tab"); + assertEq(lot, 0, "LockstakeIlkIntegration/non-zero-lot"); + assertEq(vat.dai(keeper2), 0, "LockstakeIlkIntegration/non-zero-dai"); + assertGt(token.balanceOf(keeper2), 0, "LockstakeIlkIntegration/unexpected-zero-gem-after-take"); + } + } + function _checkUNILPIntegration( bytes32 _ilk, GemJoinAbstract join, diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index f223bf61d..bc4d0e616 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -281,6 +281,22 @@ contract DssSpellTest is DssSpellTestBase { ); } + function testLockstakeIlkIntegration() public { // add the `skipped` modifier to skip + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + _checkLockstakeIlkIntegration( + LockstakeIlkParams({ + ilk: "LSE-MKR-A", + clip: addr.addr("LOCKSTAKE_CLIP"), + calc: addr.addr("LOCKSTAKE_CLIP_CALC"), + pip: addr.addr("PIP_MKR"), + ilkAmt: 1_000 * WAD + }) + ); + } + function testLerpSurplusBuffer() public skipped { // add the `skipped` modifier to skip _vote(address(spell)); _scheduleWaitAndCast(address(spell)); @@ -301,23 +317,6 @@ contract DssSpellTest is DssSpellTestBase { assertTrue(lerp.done()); } - function testNewIlkRegistryValues() public skipped { // add the `skipped` modifier to skip - _vote(address(spell)); - _scheduleWaitAndCast(address(spell)); - assertTrue(spell.done(), "TestError/spell-not-done"); - - // Insert new ilk registry values tests here - _checkIlkIntegration( - "TOKEN-X", - GemJoinAbstract(addr.addr("MCD_JOIN_TOKEN_X")), - ClipAbstract(addr.addr("MCD_CLIP_TOKEN_X")), - addr.addr("PIP_TOKEN"), - true, - true, - false - ); - } - function testEsmAuth() public skipped { // add the `skipped` modifier to skip string[1] memory esmAuthorisedContractKeys = [ "MCD_LITE_PSM_USDC_A_IN_CDT_JAR" From f82802fb6f8f48c9bf1576630b27fb07855a6fda Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Tue, 15 Oct 2024 21:27:53 +0200 Subject: [PATCH 20/44] updated testLockstakeIlkIntegration --- src/DssSpell.t.base.sol | 191 ++++++++++++++++------------------------ src/DssSpell.t.sol | 8 +- 2 files changed, 80 insertions(+), 119 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index a78ad500f..417fef2e6 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -252,6 +252,7 @@ interface LockstakeEngineLike { function open(uint256 index) external returns (address urn); function draw(address owner, uint256 index, address to, uint256 wad) external; function lock(address owner, uint256 index, uint256 wad, uint16 ref) external; + function jug() external view returns (address); } interface LockstakeClipperLike { @@ -268,6 +269,11 @@ interface LockstakeClipperLike { external view returns (uint256 pos, uint256 tab, uint256 lot, uint256 tot, address usr, uint96 tic, uint256 top); + function stopped() external view returns (uint256); +} + +interface VoteDelegateFactoryLike { + function create() external returns (address voteDelegate); } contract DssSpellTestBase is Config, DssTest { @@ -297,6 +303,7 @@ contract DssSpellTestBase is Config, DssTest { DSTokenAbstract gov = DSTokenAbstract( addr.addr("MCD_GOV")); DSTokenAbstract mkr = DSTokenAbstract( addr.addr("MCD_GOV")); GemAbstract sky = GemAbstract( addr.addr("SKY")); + MkrSkyLike mkrSky = MkrSkyLike( addr.addr("MKR_SKY")); EndAbstract end = EndAbstract( addr.addr("MCD_END")); ESMAbstract esm = ESMAbstract( addr.addr("MCD_ESM")); CureAbstract cure = CureAbstract( addr.addr("MCD_CURE")); @@ -317,6 +324,7 @@ contract DssSpellTestBase is Config, DssTest { VestAbstract vestMkr = VestAbstract( addr.addr("MCD_VEST_MKR_TREASURY")); VestAbstract vestSky = VestAbstract( addr.addr("MCD_VEST_SKY")); RwaLiquidationLike liquidationOracle = RwaLiquidationLike( addr.addr("MIP21_LIQUIDATION_ORACLE")); + address voteDelegateFactory = addr.addr("VOTE_DELEGATE_FACTORY"); DssSpell spell; @@ -1208,134 +1216,86 @@ contract DssSpellTestBase is Config, DssTest { struct LockstakeIlkParams { bytes32 ilk; + address pip; + address gem; + address lsgem; + address engine; address clip; address calc; - address pip; - uint256 ilkAmt; } function _checkLockstakeIlkIntegration( LockstakeIlkParams memory p ) internal { - assertEq(vat.dai(address(this)), 0, "LockstakeIlkIntegration/non-zero-initial-dai"); - // Check that all contracts are set + LockstakeEngineLike engine = LockstakeEngineLike(p.engine); + // Check relevant contracts are correctly configured { - assertEq(dog.vat(), address(vat), "LockstakeIlkIntegration/invalid-dog-vat"); - assertEq(dog.vow(), address(vow), "LockstakeIlkIntegration/invalid-dog-vow"); + assertEq(dog.vat(), address(vat), "LockstakeIlkIntegration/invalid-dog-vat"); + assertEq(dog.vow(), address(vow), "LockstakeIlkIntegration/invalid-dog-vow"); (address clip,,,) = dog.ilks(p.ilk); - assertEq(clip, p.clip, "LockstakeIlkIntegration/invalid-dog-clip"); - assertEq(ClipAbstract(p.clip).ilk(), p.ilk, "LockstakeIlkIntegration/invalid-clip-ilk"); - assertEq(ClipAbstract(p.clip).vat(), address(vat), "LockstakeIlkIntegration/invalid-clip-vat"); - assertEq(ClipAbstract(p.clip).vow(), address(vow), "LockstakeIlkIntegration/invalid-clip-vow"); - assertEq(ClipAbstract(p.clip).dog(), address(dog), "LockstakeIlkIntegration/invalid-clip-dog"); - assertEq(ClipAbstract(p.clip).spotter(), address(spotter), "LockstakeIlkIntegration/invalid-clip-spotter"); - assertEq(ClipAbstract(p.clip).calc(), p.calc, "LockstakeIlkIntegration/invalid-clip-calc"); - } - // Check all required authorizations - { - assertEq(vat.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-vat-clip"); - assertEq(dog.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-dog-clip"); - assertEq(ClipAbstract(p.clip).wards(address(dog)), 1, "LockstakeIlkIntegration/missing-auth-clip-dog"); - assertEq(ClipAbstract(p.clip).wards(address(end)), 1, "LockstakeIlkIntegration/missing-auth-clip-end"); - assertEq(ClipAbstract(p.clip).wards(address(clipMom)), 1, "LockstakeIlkIntegration/missing-auth--cliclippMom"); - } - // Check OSM buds - { - assertEq(OsmAbstract(p.pip).bud(address(spotter)), 1, "LockstakeIlkIntegration/missing-spotter-bud"); - assertEq(OsmAbstract(p.pip).bud(p.clip), 1, "LockstakeIlkIntegration/missing-clip-bud"); - assertEq(OsmAbstract(p.pip).bud(address(clipMom)), 1, "LockstakeIlkIntegration/missing-clipMom-bud"); - assertEq(OsmAbstract(p.pip).bud(address(end)), 1, "LockstakeIlkIntegration/missing-end-bud"); - } - // Prepare for liquidation - { - // Force max Hole - vm.store( - address(dog), - bytes32(uint256(4)), - bytes32(type(uint256).max) - ); - // Initially this test assume that's we are using freshly deployed Cliiper contract without any past auctions - if (ClipAbstract(p.clip).kicks() > 0) { - vm.store( - p.clip, - bytes32(uint256(10)), - bytes32(uint256(0)) - ); - assertEq(ClipAbstract(p.clip).kicks(), 0, "LockstakeIlkIntegration/unchanged-kicks"); - } - } - // Open new vault - LockstakeEngineLike engine = LockstakeEngineLike(LockstakeClipperLike(p.clip).engine()); - address urn = engine.open(0); - GemAbstract token = GemAbstract(reg.gem(p.ilk)); - // Lock collateral + assertEq(clip, p.clip, "LockstakeIlkIntegration/invalid-dog-clip"); + assertEq(engine.voteDelegateFactory(), voteDelegateFactory, "LockstakeIlkIntegration/invalid-engine-voteDelegateFactory"); + assertEq(engine.usdsJoin(), address(usdsJoin), "LockstakeIlkIntegration/invalid-engine-usdsJoin"); + assertEq(engine.ilk(), p.ilk, "LockstakeIlkIntegration/invalid-engine-ilk"); + assertEq(engine.mkrSky(), address(mkrSky), "LockstakeIlkIntegration/invalid-engine-mkrSky"); + assertEq(engine.lsmkr(), p.lsgem, "LockstakeIlkIntegration/invalid-engine-lsmkr"); + assertEq(engine.jug(), address(jug), "LockstakeIlkIntegration/invalid-engine-jug"); + assertEq(ClipAbstract(p.clip).vat(), address(vat), "LockstakeIlkIntegration/invalid-clip-vat"); + assertEq(ClipAbstract(p.clip).spotter(), address(spotter), "LockstakeIlkIntegration/invalid-clip-spotter"); + assertEq(ClipAbstract(p.clip).dog(), address(dog), "LockstakeIlkIntegration/invalid-clip-dog"); + assertEq(ClipAbstract(p.clip).ilk(), p.ilk, "LockstakeIlkIntegration/invalid-clip-ilk"); + assertEq(ClipAbstract(p.clip).vow(), address(vow), "LockstakeIlkIntegration/invalid-clip-vow"); + assertEq(ClipAbstract(p.clip).calc(), p.calc, "LockstakeIlkIntegration/invalid-clip-calc"); + assertEq(LockstakeClipperLike(p.clip).engine(), p.engine, "LockstakeIlkIntegration/invalid-clip-engine"); + assertEq(LockstakeClipperLike(p.clip).stopped(), 0, "LockstakeIlkIntegration/invalid-clip-stopped"); + assertEq(osmMom.osms(p.ilk), p.pip, "LockstakeIlkIntegration/invalid-osmMom-pip"); + } + // Check ilk registry values { - uint256 tknAmt = p.ilkAmt / 10 ** (18 - token.decimals()); - _giveTokens(address(token), tknAmt); - assertEq(token.balanceOf(address(this)), tknAmt, "LockstakeIlkIntegration/unchanged-balance"); - token.approve(address(engine), tknAmt); - engine.lock(address(this), 0, tknAmt, 0); - } - // Simulate normal operation of the OSM - OsmAbstract(p.pip).poke(); - vm.warp(block.timestamp + 1 hours); - OsmAbstract(p.pip).poke(); - // Generate new DAI to force a liquidation - int256 art; - uint256 rate; - { - uint256 line; - uint256 spot; - spotter.poke(p.ilk); - jug.drip(p.ilk); - (, rate, spot, line,) = vat.ilks(p.ilk); - art = int256(p.ilkAmt * spot / rate); - assertGt(art, 0, "LockstakeIlkIntegration/art-is-zero"); - _setIlkLine(p.ilk, type(uint256).max); - engine.draw(address(this), 0, address(this), uint256(art)); - _setIlkLine(p.ilk, line); - } - // Liquidate the vault - address keeper1 = address(111); - { - _setIlkMat(p.ilk, 100000 * RAY); - vm.warp(block.timestamp + 10 days); - spotter.poke(p.ilk); - assertEq(ClipAbstract(p.clip).kicks(), 0, "LockstakeIlkIntegration/unexpected-kicks-before"); - dog.bark(p.ilk, urn, keeper1); - assertEq(ClipAbstract(p.clip).kicks(), 1, "LockstakeIlkIntegration/unexpected-kicks-after"); - - (, rate,,,) = vat.ilks(p.ilk); - uint256 debt = rate * uint256(art) * dog.chop(p.ilk) / WAD; - vm.store( - address(vat), - keccak256(abi.encode(address(this), uint256(5))), - bytes32(debt) - ); - assertEq(vat.gem(p.ilk, p.clip), p.ilkAmt, "LockstakeIlkIntegration/unexpected-clip-gem-amt"); - assertGt(vat.dai(keeper1), 0, "LockstakeIlkIntegration/unexpected-zero-dai-after-bark"); - } - // Take auction - address keeper2 = address(222); + ( + string memory name, + string memory symbol, + uint256 _class, + uint256 decimals, + address gem, + address pip, + address gemJoin, + address clip + ) = reg.info(p.ilk); + assertEq(name, GemAbstract(p.lsgem).name(), "LockstakeIlkIntegration/incorrect-reg-name"); + assertEq(symbol, GemAbstract(p.lsgem).symbol(), "LockstakeIlkIntegration/incorrect-reg-symbol"); + assertEq(_class, 7, "LockstakeIlkIntegration/incorrect-reg-class"); // REG_CLASS_JOINLESS + assertEq(decimals, GemAbstract(p.lsgem).decimals(), "LockstakeIlkIntegration/incorrect-reg-dec"); + assertEq(gem, p.gem, "LockstakeIlkIntegration/incorrect-reg-gem"); + assertEq(pip, p.pip, "LockstakeIlkIntegration/incorrect-reg-pip"); + assertEq(gemJoin, address(0), "LockstakeIlkIntegration/incorrect-reg-gemJoin"); + assertEq(clip, p.clip, "LockstakeIlkIntegration/incorrect-reg-xlip"); + } + // Check required authorizations { - vm.warp(block.timestamp + 20 minutes); - (, uint256 tab, uint256 lot,,,, uint256 top) = LockstakeClipperLike(p.clip).sales(1); - - assertEq(lot, p.ilkAmt, "LockstakeIlkIntegration/unexpected-lot"); - assertGt(lot * top, tab, "LockstakeIlkIntegration/not-enough-to-cover-debt"); - - vat.hope(p.clip); - ClipAbstract(p.clip).take(1, lot, top, keeper2, bytes("")); - } - // Check values after take + assertEq(vat.wards(p.engine), 1, "LockstakeIlkIntegration/missing-auth-vat-engine"); + assertEq(vat.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-vat-clip"); + assertEq(WardsAbstract(p.pip).wards(address(osmMom)), 1, "LockstakeIlkIntegration/missing-auth-pip-osmMom"); + assertEq(dog.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-dog-clip"); + assertEq(WardsAbstract(p.lsgem).wards(p.engine), 1, "LockstakeIlkIntegration/missing-auth-lsgem-engine"); + assertEq(WardsAbstract(p.engine).wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-engine-clip"); + assertEq(WardsAbstract(p.clip).wards(address(dog)), 1, "LockstakeIlkIntegration/missing-auth-clip-dog"); + assertEq(WardsAbstract(p.clip).wards(address(end)), 1, "LockstakeIlkIntegration/missing-auth-clip-end"); + assertEq(WardsAbstract(p.clip).wards(address(clipMom)), 1, "LockstakeIlkIntegration/missing-auth-clip-clipMom"); + } + // Check required OSM buds { - (, uint256 tab, uint256 lot,, address usr,,) = LockstakeClipperLike(p.clip).sales(1); - assertEq(usr, address(0), "LockstakeIlkIntegration/unexpected-usr"); - assertEq(tab, 0, "LockstakeIlkIntegration/non-zero-tab"); - assertEq(lot, 0, "LockstakeIlkIntegration/non-zero-lot"); - assertEq(vat.dai(keeper2), 0, "LockstakeIlkIntegration/non-zero-dai"); - assertGt(token.balanceOf(keeper2), 0, "LockstakeIlkIntegration/unexpected-zero-gem-after-take"); + assertEq(OsmAbstract(p.pip).bud(address(spotter)), 1, "LockstakeIlkIntegration/missing-spotter-bud"); + assertEq(OsmAbstract(p.pip).bud(p.clip), 1, "LockstakeIlkIntegration/missing-clip-bud"); + assertEq(OsmAbstract(p.pip).bud(address(clipMom)), 1, "LockstakeIlkIntegration/missing-clipMom-bud"); + assertEq(OsmAbstract(p.pip).bud(address(end)), 1, "LockstakeIlkIntegration/missing-end-bud"); } + // TODO: + // create vault + // lock and free mkr and sky + // draw and wipe + // farm and get a reward + // liquidate with farming and delegating } function _checkUNILPIntegration( @@ -2877,7 +2837,6 @@ contract DssSpellTestBase is Config, DssTest { // Converter: MKR <-> SKY { - MkrSkyLike mkrSky = MkrSkyLike(addr.addr("MKR_SKY")); address mkrHolder = address(0x42); deal(address(gov), mkrHolder, 1_000 * WAD); address skyHolder = address(0x65); diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index bc4d0e616..c0f6073a4 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -289,10 +289,12 @@ contract DssSpellTest is DssSpellTestBase { _checkLockstakeIlkIntegration( LockstakeIlkParams({ ilk: "LSE-MKR-A", - clip: addr.addr("LOCKSTAKE_CLIP"), - calc: addr.addr("LOCKSTAKE_CLIP_CALC"), pip: addr.addr("PIP_MKR"), - ilkAmt: 1_000 * WAD + gem: addr.addr("MCD_GOV"), + lsgem: addr.addr("LOCKSTAKE_MKR"), + engine: addr.addr("LOCKSTAKE_ENGINE"), + clip: addr.addr("LOCKSTAKE_CLIP"), + calc: addr.addr("LOCKSTAKE_CLIP_CALC") }) ); } From a1bffc3709fe083106b29953c0e06284e71ef068 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Tue, 15 Oct 2024 21:30:16 +0200 Subject: [PATCH 21/44] small fixes --- src/DssSpell.sol | 2 +- src/DssSpell.t.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index b849cc17a..1b0ec225f 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -329,7 +329,7 @@ contract DssSpellAction is DssAction { // LockstakeClipper will be authorized to access "vat" and LockstakeEngine // CLIPPER_MOM, MCD_DOG and MCD_END will be authorized to access LockstakeClipper // New chainlog keys LOCKSTAKE_MKR, LOCKSTAKE_ENGINE, LOCKSTAKE_CLIP and LOCKSTAKE_CLIP_CALC will be added - // Note: above instructions are taken inside FlapperInit.setFarm method + // Note: above instructions are taken inside LockstakeInit.initLockstake method // ---------- Fund Early Bird Rewards Multisig ---------- // Forum: https://forum.sky.money/t/atlas-weekly-cycle-edit-proposal-week-of-october-14-2024-01/25324#p-99402-early-bird-bonus-3 diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index c0f6073a4..f69fe72af 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -672,7 +672,7 @@ contract DssSpellTest is DssSpellTestBase { sky: int256(sky.totalSupply()) - previousTotalSupply.sky }); assertEq( - actualTotalDiff.dai, + actualTotalDiff.dai + actualTotalDiff.usds, calculatedTotalDiff.dai + calculatedTotalDiff.usds, "TestPayments/invalid-dai-sky-total" ); From 7380cbd320f19a0779a1d1953a8cc9aae9634b31 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Wed, 16 Oct 2024 10:40:20 +0200 Subject: [PATCH 22/44] test lockstake locking, drawing, barking, taking --- src/DssSpell.t.base.sol | 213 ++++++++++++++++++++++++++++++++++++---- src/DssSpell.t.sol | 3 +- 2 files changed, 194 insertions(+), 22 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 417fef2e6..037a37f14 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -234,25 +234,53 @@ interface StakingRewardsLike { } interface LockstakeEngineLike { - function voteDelegateFactory() external view returns (address); - function vat() external view returns (address); - function usdsJoin() external view returns (address); - function usds() external view returns (address); + function addFarm(address farm) external; + function delFarm(address farm) external; + function deny(address usr) external; + function draw(address owner, uint256 index, address to, uint256 wad) external; + function farms(address farm) external view returns (uint8 farmStatus); + function fee() external view returns (uint256); + function file(bytes32 what, uint256 data) external; + function file(bytes32 what, address data) external; + function free(address owner, uint256 index, address to, uint256 wad) external returns (uint256 freed); + function freeNoFee(address owner, uint256 index, address to, uint256 wad) external; + function freeSky(address owner, uint256 index, address to, uint256 skyWad) external returns (uint256 skyFreed); + function getReward(address owner, uint256 index, address farm, address to) external returns (uint256 amt); + function hope(address owner, uint256 index, address usr) external; function ilk() external view returns (bytes32); - function mkr() external view returns (address); + function isUrnAuth(address owner, uint256 index, address usr) external view returns (bool ok); + function jug() external view returns (address); + function lock(address owner, uint256 index, uint256 wad, uint16 ref) external; + function lockSky(address owner, uint256 index, uint256 skyWad, uint16 ref) external; function lsmkr() external view returns (address); - function fee() external view returns (uint256); + function mkr() external view returns (address); function mkrSky() external view returns (address); - function sky() external view returns (address); - function rely(address) external; - function file(bytes32, address) external; - function file(bytes32, uint256) external; - function addFarm(address) external; - function farms(address farm) external view returns (uint8); + function mkrSkyRate() external view returns (uint256); + function multicall(bytes[] memory data) external returns (bytes[] memory results); + function nope(address owner, uint256 index, address usr) external; + function onKick(address urn, uint256 wad) external; + function onRemove(address urn, uint256 sold, uint256 left) external; + function onTake(address urn, address who, uint256 wad) external; function open(uint256 index) external returns (address urn); - function draw(address owner, uint256 index, address to, uint256 wad) external; - function lock(address owner, uint256 index, uint256 wad, uint16 ref) external; - function jug() external view returns (address); + function ownerUrns(address owner, uint256 index) external view returns (address urn); + function ownerUrnsCount(address owner) external view returns (uint256 count); + function rely(address usr) external; + function selectFarm(address owner, uint256 index, address farm, uint16 ref) external; + function selectVoteDelegate(address owner, uint256 index, address voteDelegate) external; + function sky() external view returns (address); + function urnAuctions(address urn) external view returns (uint256 auctionsCount); + function urnCan(address urn, address usr) external view returns (uint256 allowed); + function urnFarms(address urn) external view returns (address farm); + function urnImplementation() external view returns (address); + function urnOwners(address urn) external view returns (address owner); + function urnVoteDelegates(address urn) external view returns (address voteDelegate); + function usds() external view returns (address); + function usdsJoin() external view returns (address); + function vat() external view returns (address); + function voteDelegateFactory() external view returns (address); + function wards(address usr) external view returns (uint256 allowed); + function wipe(address owner, uint256 index, uint256 wad) external; + function wipeAll(address owner, uint256 index) external returns (uint256 wad); } interface LockstakeClipperLike { @@ -1222,6 +1250,7 @@ contract DssSpellTestBase is Config, DssTest { address engine; address clip; address calc; + address farm; } function _checkLockstakeIlkIntegration( @@ -1230,6 +1259,7 @@ contract DssSpellTestBase is Config, DssTest { LockstakeEngineLike engine = LockstakeEngineLike(p.engine); // Check relevant contracts are correctly configured { + assertEq(p.gem, address(mkr), "LockstakeIlkIntegration/invalid-gem"); assertEq(dog.vat(), address(vat), "LockstakeIlkIntegration/invalid-dog-vat"); assertEq(dog.vow(), address(vow), "LockstakeIlkIntegration/invalid-dog-vow"); (address clip,,,) = dog.ilks(p.ilk); @@ -1290,12 +1320,153 @@ contract DssSpellTestBase is Config, DssTest { assertEq(OsmAbstract(p.pip).bud(address(clipMom)), 1, "LockstakeIlkIntegration/missing-clipMom-bud"); assertEq(OsmAbstract(p.pip).bud(address(end)), 1, "LockstakeIlkIntegration/missing-end-bud"); } - // TODO: - // create vault - // lock and free mkr and sky - // draw and wipe - // farm and get a reward - // liquidate with farming and delegating + // Prepare for liquidation + uint256 drawAmt; + uint256 lockAmt; + { + // Force max Hole + vm.store(address(dog), bytes32(uint256(4)), bytes32(type(uint256).max)); + // Reset auction count + if (ClipAbstract(p.clip).kicks() > 0) { + vm.store(p.clip, bytes32(uint256(10)), bytes32(uint256(0))); + assertEq(ClipAbstract(p.clip).kicks(), 0, "LockstakeIlkIntegration/unchanged-kicks"); + } + // Give tokens + _giveTokens(address(mkr), 100_000 * 10**18); + _giveTokens(address(sky), 100_000 * 10**18); + // Poke OSM price + OsmAbstract(p.pip).poke(); + vm.warp(block.timestamp + 1 hours); + OsmAbstract(p.pip).poke(); + spotter.poke(p.ilk); + // Calculate lock and draw amounts + (,,,, uint256 dust) = vat.ilks(p.ilk); + drawAmt = dust / RAY; + lockAmt = drawAmt / 2; + } + uint256 snapshot = vm.snapshot(); + _checkLockstakeTake(p, lockAmt, drawAmt, false, false); vm.revertTo(snapshot); + _checkLockstakeTake(p, lockAmt, drawAmt, false, true); vm.revertTo(snapshot); + + // TODO: fix tests for liquidation of the delegated tokens + // _checkLockstakeTake(p, lockAmt, drawAmt, true, false); vm.revertTo(snapshot); + // _checkLockstakeTake(p, lockAmt, drawAmt, true, true); vm.revertTo(snapshot); + + // TODO: add more coverange + // - lock and free mkr and sky + // - draw and wipe + // - farm and get a reward + // - Liquidate with farming and delegating + } + + function _ink(bytes32 ilk_, address urn) internal view returns (uint256 ink) { + (ink,) = vat.urns(ilk_, urn); + } + + function _art(bytes32 ilk_, address urn) internal view returns (uint256 art) { + (, art) = vat.urns(ilk_, urn); + } + + struct Sale { + uint256 pos; // Index in active array + uint256 tab; // Dai to raise [rad] + uint256 lot; // collateral to sell [wad] + uint256 tot; // static registry of total collateral to sell [wad] + address usr; // Liquidated CDP + uint96 tic; // Auction start time + uint256 top; // Starting price [ray] + } + + function _checkLockstakeTake( + LockstakeIlkParams memory p, + uint256 lockAmt, + uint256 drawAmt, + bool withDelegate, + bool withStaking + ) internal { + // Open vault + LockstakeEngineLike engine = LockstakeEngineLike(p.engine); + vm.prank(address(123)); address voteDelegate = VoteDelegateFactoryLike(voteDelegateFactory).create(); + assertNotEq(voteDelegate, address(0), "LockstakeTake/invalid-voteDelegate-address"); + address urn = engine.open(0); + + // Lock and draw + if (withDelegate) { + engine.selectVoteDelegate(address(this), 0, voteDelegate); + } + if (withStaking) { + engine.selectFarm(address(this), 0, address(p.farm), 0); + } + mkr.approve(address(engine), lockAmt); + engine.lock(address(this), 0, lockAmt, 0); + vm.warp(block.timestamp + 1); + engine.draw(address(this), 0, address(this), drawAmt); + if (withDelegate) { + assertEq(engine.urnVoteDelegates(urn), voteDelegate, "LockstakeTake/AfterLockDraw/withDelegate/invalid-voteDelegate-urn"); + assertEq(mkr.balanceOf(voteDelegate), lockAmt, "LockstakeTake/AfterLockDraw/withDelegate/invalid-voteDelegate-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), 0, "LockstakeTake/AfterLockDraw/withDelegate/invalid-engine-balance"); + } else { + assertEq(engine.urnVoteDelegates(urn), address(0), "LockstakeTake/AfterLockDraw/withoutDelegate/invalid-voteDelegate-urn"); + assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/AfterLockDraw/withoutDelegate/invalid-engine-balance"); + } + if (withStaking) { + assertEq(GemAbstract(p.lsgem).balanceOf(urn), 0, "LockstakeTake/AfterLockDraw/withStaking/invalid-urn-lsgem-balance"); + assertEq(GemAbstract(p.lsgem).balanceOf(p.farm), lockAmt, "LockstakeTake/AfterLockDraw/withStaking/invalid-farm-lsgem-balance"); + assertEq(GemAbstract(p.farm).balanceOf(urn), lockAmt, "LockstakeTake/AfterLockDraw/withStaking/invalid-urn-farm-balance"); + } else { + assertEq(GemAbstract(p.lsgem).balanceOf(urn), lockAmt, "LockstakeTake/AfterLockDraw/withoutStaking/invalid-urn-lsgem-balance"); + } + + // Force liquidation + uint256 lsgemInitialSupply = GemAbstract(p.lsgem).totalSupply(); + _setIlkMat(p.ilk, 100_000 * RAY); + spotter.poke(p.ilk); + assertEq(ClipAbstract(p.clip).kicks(), 0, "LockstakeTake/non-0-kicks"); + assertEq(engine.urnAuctions(urn), 0, "LockstakeTake/non-0-actions"); + uint256 id = dog.bark(p.ilk, urn, address(this)); + assertEq(ClipAbstract(p.clip).kicks(), 1, "LockstakeTake/AfterBark/no-kicks"); + assertEq(engine.urnAuctions(urn), 1, "LockstakeTake/AfterBark/no-actions"); + Sale memory sale; + (sale.pos, sale.tab, sale.lot, sale.tot, sale.usr, sale.tic, sale.top) = LockstakeClipperLike(p.clip).sales(id); + assertEq(sale.pos, 0, "LockstakeTake/AfterBark/invalid-sale.pos"); + assertGt(sale.tab, drawAmt * RAY, "LockstakeTake/AfterBark/invalid-sale.tab"); + assertEq(sale.lot, lockAmt, "LockstakeTake/AfterBark/invalid-sale.lot"); + assertEq(sale.tot, lockAmt, "LockstakeTake/AfterBark/invalid-sale.tot"); + assertEq(sale.usr, urn, "LockstakeTake/AfterBark/invalid-sale.usr"); + assertEq(sale.tic, block.timestamp, "LockstakeTake/AfterBark/invalid-sale.tic"); + assertEq(sale.top, _getOSMPrice(p.pip) * ClipAbstract(p.clip).buf() / WAD, "LockstakeTake/AfterBark/invalid-sale.top"); + assertEq(vat.gem(p.ilk, p.clip), lockAmt, "LockstakeTake/AfterBark/invalid-vat-gem-clip"); + if (withDelegate) { + assertEq(mkr.balanceOf(voteDelegate), 0, "LockstakeTake/AfterBark/withDelegate/invalid-voteDelegate-mkr-balance"); + } + assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/AfterBark/invalid-engine-mkr-balance"); + if (withStaking) { + assertEq(GemAbstract(p.lsgem).balanceOf(p.farm), 0, "LockstakeTake/AfterBark/withStaking/invalid-farm-lsgem-balance"); + assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "LockstakeTake/AfterBark/withStaking/invalid-urn-farm-balance"); + } + assertEq(GemAbstract(p.lsgem).balanceOf(urn), 0, "LockstakeTake/AfterBark/invalid-urn-lsgem-balance"); + assertEq(GemAbstract(p.lsgem).totalSupply(), lsgemInitialSupply - lockAmt, "LockstakeTake/AfterBark/invalid-lsgem-totalSupply"); + + // Take auction + address buyer = address(888); + vm.prank(pauseProxy); vat.suck(address(0), buyer, sale.tab); + vm.prank(buyer); vat.hope(p.clip); + assertEq(mkr.balanceOf(buyer), 0, "LockstakeTake/AfterBark/invalid-buyer-mkr-balance"); + vm.prank(buyer); ClipAbstract(p.clip).take(id, lockAmt, type(uint256).max, buyer, ""); + assertGt(mkr.balanceOf(buyer), 0, "LockstakeTake/AfterTake/invalid-buyer-mkr-balance"); + (sale.pos, sale.tab, sale.lot, sale.tot, sale.usr, sale.tic, sale.top) = LockstakeClipperLike(p.clip).sales(id); + assertEq(sale.pos, 0, "LockstakeTake/AfterTake/invalid-sale.pos"); + assertEq(sale.tab, 0, "LockstakeTake/AfterTake/invalid-sale.tab"); + assertEq(sale.lot, 0, "LockstakeTake/AfterTake/invalid-sale.lot"); + assertEq(sale.tot, 0, "LockstakeTake/AfterTake/invalid-sale.tot"); + assertEq(sale.usr, address(0), "LockstakeTake/AfterTake/invalid-sale.usr"); + assertEq(sale.tic, 0, "LockstakeTake/AfterTake/invalid-sale.tic"); + assertEq(sale.top, 0, "LockstakeTake/AfterTake/invalid-sale.top"); + assertEq(vat.gem(p.ilk, p.clip), 0, "LockstakeTake/AfterTake/invalid-vat.gem"); + if (withStaking) { + assertEq(GemAbstract(p.lsgem).balanceOf(p.farm), 0, "LockstakeTake/AfterTake/withStaking/invalid-lsgem-farm-balance"); + assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "LockstakeTake/AfterTake/withStaking/invalid-farm-urn-balance"); + } } function _checkUNILPIntegration( diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index f69fe72af..c63be7fb2 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -294,7 +294,8 @@ contract DssSpellTest is DssSpellTestBase { lsgem: addr.addr("LOCKSTAKE_MKR"), engine: addr.addr("LOCKSTAKE_ENGINE"), clip: addr.addr("LOCKSTAKE_CLIP"), - calc: addr.addr("LOCKSTAKE_CLIP_CALC") + calc: addr.addr("LOCKSTAKE_CLIP_CALC"), + farm: addr.addr("REWARDS_LSMKR_USDS") }) ); } From f5dc8394aec7b3836094f98b3f5d1b23b32decce Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Wed, 16 Oct 2024 11:04:06 +0200 Subject: [PATCH 23/44] test _checkLockstakeTake with delegation --- src/DssSpell.t.base.sol | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 037a37f14..e53069991 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -1347,10 +1347,8 @@ contract DssSpellTestBase is Config, DssTest { uint256 snapshot = vm.snapshot(); _checkLockstakeTake(p, lockAmt, drawAmt, false, false); vm.revertTo(snapshot); _checkLockstakeTake(p, lockAmt, drawAmt, false, true); vm.revertTo(snapshot); - - // TODO: fix tests for liquidation of the delegated tokens - // _checkLockstakeTake(p, lockAmt, drawAmt, true, false); vm.revertTo(snapshot); - // _checkLockstakeTake(p, lockAmt, drawAmt, true, true); vm.revertTo(snapshot); + _checkLockstakeTake(p, lockAmt, drawAmt, true, false); vm.revertTo(snapshot); + _checkLockstakeTake(p, lockAmt, drawAmt, true, true); vm.revertTo(snapshot); // TODO: add more coverange // - lock and free mkr and sky @@ -1397,13 +1395,13 @@ contract DssSpellTestBase is Config, DssTest { if (withStaking) { engine.selectFarm(address(this), 0, address(p.farm), 0); } + uint256 previousCheifBalance = mkr.balanceOf(address(chief)); mkr.approve(address(engine), lockAmt); engine.lock(address(this), 0, lockAmt, 0); - vm.warp(block.timestamp + 1); engine.draw(address(this), 0, address(this), drawAmt); if (withDelegate) { assertEq(engine.urnVoteDelegates(urn), voteDelegate, "LockstakeTake/AfterLockDraw/withDelegate/invalid-voteDelegate-urn"); - assertEq(mkr.balanceOf(voteDelegate), lockAmt, "LockstakeTake/AfterLockDraw/withDelegate/invalid-voteDelegate-mkr-balance"); + assertEq(mkr.balanceOf(address(chief)) - previousCheifBalance, lockAmt, "LockstakeTake/AfterLockDraw/withDelegate/invalid-chief-mkr-balance"); assertEq(mkr.balanceOf(p.engine), 0, "LockstakeTake/AfterLockDraw/withDelegate/invalid-engine-balance"); } else { assertEq(engine.urnVoteDelegates(urn), address(0), "LockstakeTake/AfterLockDraw/withoutDelegate/invalid-voteDelegate-urn"); @@ -1418,7 +1416,9 @@ contract DssSpellTestBase is Config, DssTest { } // Force liquidation - uint256 lsgemInitialSupply = GemAbstract(p.lsgem).totalSupply(); + if (withDelegate) { + vm.roll(block.number + 1); // Roll one block to allow freeing from cheif + } _setIlkMat(p.ilk, 100_000 * RAY); spotter.poke(p.ilk); assertEq(ClipAbstract(p.clip).kicks(), 0, "LockstakeTake/non-0-kicks"); @@ -1445,7 +1445,6 @@ contract DssSpellTestBase is Config, DssTest { assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "LockstakeTake/AfterBark/withStaking/invalid-urn-farm-balance"); } assertEq(GemAbstract(p.lsgem).balanceOf(urn), 0, "LockstakeTake/AfterBark/invalid-urn-lsgem-balance"); - assertEq(GemAbstract(p.lsgem).totalSupply(), lsgemInitialSupply - lockAmt, "LockstakeTake/AfterBark/invalid-lsgem-totalSupply"); // Take auction address buyer = address(888); From 0781353e82e47e444126360825b59eab2a735535 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Wed, 16 Oct 2024 11:21:19 +0200 Subject: [PATCH 24/44] cleanup tests --- src/DssSpell.t.base.sol | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index e53069991..c0801306a 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -1331,9 +1331,6 @@ contract DssSpellTestBase is Config, DssTest { vm.store(p.clip, bytes32(uint256(10)), bytes32(uint256(0))); assertEq(ClipAbstract(p.clip).kicks(), 0, "LockstakeIlkIntegration/unchanged-kicks"); } - // Give tokens - _giveTokens(address(mkr), 100_000 * 10**18); - _giveTokens(address(sky), 100_000 * 10**18); // Poke OSM price OsmAbstract(p.pip).poke(); vm.warp(block.timestamp + 1 hours); @@ -1343,6 +1340,9 @@ contract DssSpellTestBase is Config, DssTest { (,,,, uint256 dust) = vat.ilks(p.ilk); drawAmt = dust / RAY; lockAmt = drawAmt / 2; + // Give tokens + _giveTokens(address(mkr), lockAmt); + _giveTokens(address(sky), lockAmt * afterSpell.sky_mkr_rate); } uint256 snapshot = vm.snapshot(); _checkLockstakeTake(p, lockAmt, drawAmt, false, false); vm.revertTo(snapshot); @@ -1357,14 +1357,6 @@ contract DssSpellTestBase is Config, DssTest { // - Liquidate with farming and delegating } - function _ink(bytes32 ilk_, address urn) internal view returns (uint256 ink) { - (ink,) = vat.urns(ilk_, urn); - } - - function _art(bytes32 ilk_, address urn) internal view returns (uint256 art) { - (, art) = vat.urns(ilk_, urn); - } - struct Sale { uint256 pos; // Index in active array uint256 tab; // Dai to raise [rad] @@ -1395,17 +1387,17 @@ contract DssSpellTestBase is Config, DssTest { if (withStaking) { engine.selectFarm(address(this), 0, address(p.farm), 0); } - uint256 previousCheifBalance = mkr.balanceOf(address(chief)); - mkr.approve(address(engine), lockAmt); + uint256 previousCheifBalance = GemAbstract(p.gem).balanceOf(address(chief)); + GemAbstract(p.gem).approve(address(engine), lockAmt); engine.lock(address(this), 0, lockAmt, 0); engine.draw(address(this), 0, address(this), drawAmt); if (withDelegate) { assertEq(engine.urnVoteDelegates(urn), voteDelegate, "LockstakeTake/AfterLockDraw/withDelegate/invalid-voteDelegate-urn"); - assertEq(mkr.balanceOf(address(chief)) - previousCheifBalance, lockAmt, "LockstakeTake/AfterLockDraw/withDelegate/invalid-chief-mkr-balance"); - assertEq(mkr.balanceOf(p.engine), 0, "LockstakeTake/AfterLockDraw/withDelegate/invalid-engine-balance"); + assertEq(GemAbstract(p.gem).balanceOf(address(chief)) - previousCheifBalance, lockAmt, "LockstakeTake/AfterLockDraw/withDelegate/invalid-chief-mkr-balance"); + assertEq(GemAbstract(p.gem).balanceOf(p.engine), 0, "LockstakeTake/AfterLockDraw/withDelegate/invalid-engine-balance"); } else { assertEq(engine.urnVoteDelegates(urn), address(0), "LockstakeTake/AfterLockDraw/withoutDelegate/invalid-voteDelegate-urn"); - assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/AfterLockDraw/withoutDelegate/invalid-engine-balance"); + assertEq(GemAbstract(p.gem).balanceOf(p.engine), lockAmt, "LockstakeTake/AfterLockDraw/withoutDelegate/invalid-engine-balance"); } if (withStaking) { assertEq(GemAbstract(p.lsgem).balanceOf(urn), 0, "LockstakeTake/AfterLockDraw/withStaking/invalid-urn-lsgem-balance"); @@ -1436,23 +1428,23 @@ contract DssSpellTestBase is Config, DssTest { assertEq(sale.tic, block.timestamp, "LockstakeTake/AfterBark/invalid-sale.tic"); assertEq(sale.top, _getOSMPrice(p.pip) * ClipAbstract(p.clip).buf() / WAD, "LockstakeTake/AfterBark/invalid-sale.top"); assertEq(vat.gem(p.ilk, p.clip), lockAmt, "LockstakeTake/AfterBark/invalid-vat-gem-clip"); + assertEq(GemAbstract(p.gem).balanceOf(p.engine), lockAmt, "LockstakeTake/AfterBark/invalid-engine-mkr-balance"); + assertEq(GemAbstract(p.lsgem).balanceOf(urn), 0, "LockstakeTake/AfterBark/invalid-urn-lsgem-balance"); if (withDelegate) { - assertEq(mkr.balanceOf(voteDelegate), 0, "LockstakeTake/AfterBark/withDelegate/invalid-voteDelegate-mkr-balance"); + assertEq(GemAbstract(p.gem).balanceOf(voteDelegate), 0, "LockstakeTake/AfterBark/withDelegate/invalid-voteDelegate-mkr-balance"); } - assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/AfterBark/invalid-engine-mkr-balance"); if (withStaking) { assertEq(GemAbstract(p.lsgem).balanceOf(p.farm), 0, "LockstakeTake/AfterBark/withStaking/invalid-farm-lsgem-balance"); assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "LockstakeTake/AfterBark/withStaking/invalid-urn-farm-balance"); } - assertEq(GemAbstract(p.lsgem).balanceOf(urn), 0, "LockstakeTake/AfterBark/invalid-urn-lsgem-balance"); // Take auction address buyer = address(888); vm.prank(pauseProxy); vat.suck(address(0), buyer, sale.tab); vm.prank(buyer); vat.hope(p.clip); - assertEq(mkr.balanceOf(buyer), 0, "LockstakeTake/AfterBark/invalid-buyer-mkr-balance"); + assertEq(GemAbstract(p.gem).balanceOf(buyer), 0, "LockstakeTake/AfterBark/invalid-buyer-mkr-balance"); vm.prank(buyer); ClipAbstract(p.clip).take(id, lockAmt, type(uint256).max, buyer, ""); - assertGt(mkr.balanceOf(buyer), 0, "LockstakeTake/AfterTake/invalid-buyer-mkr-balance"); + assertGt(GemAbstract(p.gem).balanceOf(buyer), 0, "LockstakeTake/AfterTake/invalid-buyer-mkr-balance"); (sale.pos, sale.tab, sale.lot, sale.tot, sale.usr, sale.tic, sale.top) = LockstakeClipperLike(p.clip).sales(id); assertEq(sale.pos, 0, "LockstakeTake/AfterTake/invalid-sale.pos"); assertEq(sale.tab, 0, "LockstakeTake/AfterTake/invalid-sale.tab"); From 8eedd4bd064acfa8ff1e61b1f6f63b36583098ed Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Wed, 16 Oct 2024 11:36:52 +0200 Subject: [PATCH 25/44] move fee and farm checks from config.sol into integration --- src/DssSpell.t.base.sol | 51 +++++++-------- src/DssSpell.t.sol | 3 +- src/test/config.sol | 139 ---------------------------------------- 3 files changed, 25 insertions(+), 168 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index c0801306a..886c626d8 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -886,15 +886,6 @@ contract DssSpellTestBase is Config, DssTest { // check correct clipper type is used for the reg.class 7 address engine = LockstakeClipperLike(address(clip)).engine(); assertNotEq(engine, address(0), _concat("TestError/clip-engine-is-not-set-", ilk)); - // check lockstake engine fee to match config - uint256 normalisedFee = values.collaterals[ilk].engine_fee * WAD / 10_000; - assertEq(LockstakeEngineLike(engine).fee(), normalisedFee, _concat("TestError/engine-fee-", ilk)); - // check lockstake engine farms to match config - for (uint256 j = 0; j < values.collaterals[ilk].engine_farms.length; j++) { - address farm = addr.addr(values.collaterals[ilk].engine_farms[j]); - assertNotEq(farm, address(0), _concat("TestError/farm-is-empty-", ilk)); - assertEq(LockstakeEngineLike(engine).farms(farm), 1, _concat("TestError/engine-farms-", ilk)); - } } } if (reg.class(ilk) < 3) { @@ -1251,6 +1242,7 @@ contract DssSpellTestBase is Config, DssTest { address clip; address calc; address farm; + uint256 fee; } function _checkLockstakeIlkIntegration( @@ -1259,26 +1251,29 @@ contract DssSpellTestBase is Config, DssTest { LockstakeEngineLike engine = LockstakeEngineLike(p.engine); // Check relevant contracts are correctly configured { - assertEq(p.gem, address(mkr), "LockstakeIlkIntegration/invalid-gem"); - assertEq(dog.vat(), address(vat), "LockstakeIlkIntegration/invalid-dog-vat"); - assertEq(dog.vow(), address(vow), "LockstakeIlkIntegration/invalid-dog-vow"); + assertEq(p.gem, address(mkr), "LockstakeIlkIntegration/invalid-gem"); + assertEq(engine.fee(), p.fee * WAD / 10_000, "LockstakeIlkIntegration/invalid-fee"); + assertNotEq(p.farm, address(0), "LockstakeIlkIntegration/invalid-farm"); + assertEq(engine.farms(p.farm), 1, "LockstakeIlkIntegration/disabled-farm"); + assertEq(dog.vat(), address(vat), "LockstakeIlkIntegration/invalid-dog-vat"); + assertEq(dog.vow(), address(vow), "LockstakeIlkIntegration/invalid-dog-vow"); (address clip,,,) = dog.ilks(p.ilk); - assertEq(clip, p.clip, "LockstakeIlkIntegration/invalid-dog-clip"); - assertEq(engine.voteDelegateFactory(), voteDelegateFactory, "LockstakeIlkIntegration/invalid-engine-voteDelegateFactory"); - assertEq(engine.usdsJoin(), address(usdsJoin), "LockstakeIlkIntegration/invalid-engine-usdsJoin"); - assertEq(engine.ilk(), p.ilk, "LockstakeIlkIntegration/invalid-engine-ilk"); - assertEq(engine.mkrSky(), address(mkrSky), "LockstakeIlkIntegration/invalid-engine-mkrSky"); - assertEq(engine.lsmkr(), p.lsgem, "LockstakeIlkIntegration/invalid-engine-lsmkr"); - assertEq(engine.jug(), address(jug), "LockstakeIlkIntegration/invalid-engine-jug"); - assertEq(ClipAbstract(p.clip).vat(), address(vat), "LockstakeIlkIntegration/invalid-clip-vat"); - assertEq(ClipAbstract(p.clip).spotter(), address(spotter), "LockstakeIlkIntegration/invalid-clip-spotter"); - assertEq(ClipAbstract(p.clip).dog(), address(dog), "LockstakeIlkIntegration/invalid-clip-dog"); - assertEq(ClipAbstract(p.clip).ilk(), p.ilk, "LockstakeIlkIntegration/invalid-clip-ilk"); - assertEq(ClipAbstract(p.clip).vow(), address(vow), "LockstakeIlkIntegration/invalid-clip-vow"); - assertEq(ClipAbstract(p.clip).calc(), p.calc, "LockstakeIlkIntegration/invalid-clip-calc"); - assertEq(LockstakeClipperLike(p.clip).engine(), p.engine, "LockstakeIlkIntegration/invalid-clip-engine"); - assertEq(LockstakeClipperLike(p.clip).stopped(), 0, "LockstakeIlkIntegration/invalid-clip-stopped"); - assertEq(osmMom.osms(p.ilk), p.pip, "LockstakeIlkIntegration/invalid-osmMom-pip"); + assertEq(clip, p.clip, "LockstakeIlkIntegration/invalid-dog-clip"); + assertEq(engine.voteDelegateFactory(), voteDelegateFactory, "LockstakeIlkIntegration/invalid-engine-voteDelegateFactory"); + assertEq(engine.usdsJoin(), address(usdsJoin), "LockstakeIlkIntegration/invalid-engine-usdsJoin"); + assertEq(engine.ilk(), p.ilk, "LockstakeIlkIntegration/invalid-engine-ilk"); + assertEq(engine.mkrSky(), address(mkrSky), "LockstakeIlkIntegration/invalid-engine-mkrSky"); + assertEq(engine.lsmkr(), p.lsgem, "LockstakeIlkIntegration/invalid-engine-lsmkr"); + assertEq(engine.jug(), address(jug), "LockstakeIlkIntegration/invalid-engine-jug"); + assertEq(ClipAbstract(p.clip).vat(), address(vat), "LockstakeIlkIntegration/invalid-clip-vat"); + assertEq(ClipAbstract(p.clip).spotter(), address(spotter), "LockstakeIlkIntegration/invalid-clip-spotter"); + assertEq(ClipAbstract(p.clip).dog(), address(dog), "LockstakeIlkIntegration/invalid-clip-dog"); + assertEq(ClipAbstract(p.clip).ilk(), p.ilk, "LockstakeIlkIntegration/invalid-clip-ilk"); + assertEq(ClipAbstract(p.clip).vow(), address(vow), "LockstakeIlkIntegration/invalid-clip-vow"); + assertEq(ClipAbstract(p.clip).calc(), p.calc, "LockstakeIlkIntegration/invalid-clip-calc"); + assertEq(LockstakeClipperLike(p.clip).engine(), p.engine, "LockstakeIlkIntegration/invalid-clip-engine"); + assertEq(LockstakeClipperLike(p.clip).stopped(), 0, "LockstakeIlkIntegration/invalid-clip-stopped"); + assertEq(osmMom.osms(p.ilk), p.pip, "LockstakeIlkIntegration/invalid-osmMom-pip"); } // Check ilk registry values { diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index c63be7fb2..bb27eb058 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -295,7 +295,8 @@ contract DssSpellTest is DssSpellTestBase { engine: addr.addr("LOCKSTAKE_ENGINE"), clip: addr.addr("LOCKSTAKE_CLIP"), calc: addr.addr("LOCKSTAKE_CLIP_CALC"), - farm: addr.addr("REWARDS_LSMKR_USDS") + farm: addr.addr("REWARDS_LSMKR_USDS"), + fee: 5_00 }) ); } diff --git a/src/test/config.sol b/src/test/config.sol index 5a2f697e8..889ffb8d4 100644 --- a/src/test/config.sol +++ b/src/test/config.sol @@ -83,8 +83,6 @@ contract Config { uint256 calc_tau; uint256 calc_step; uint256 calc_cut; - uint256 engine_fee; - bytes32[] engine_farms; bool offboarding; } @@ -174,8 +172,6 @@ contract Config { calc_tau: 0, // In seconds calc_step: 90, // In seconds calc_cut: 9900, // In basis points - engine_fee: 0, // In basis points - engine_farms: new bytes32[](0), // Array of chainlog keys of added farms offboarding: false // If mat is being offboarded }); afterSpell.collaterals["ETH-B"] = CollateralValues({ @@ -201,8 +197,6 @@ contract Config { calc_tau: 0, calc_step: 60, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["ETH-C"] = CollateralValues({ @@ -228,8 +222,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["BAT-A"] = CollateralValues({ @@ -255,8 +247,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["USDC-A"] = CollateralValues({ @@ -282,8 +272,6 @@ contract Config { calc_tau: 4_320_000, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["USDC-B"] = CollateralValues({ @@ -309,8 +297,6 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["WBTC-A"] = CollateralValues({ @@ -336,8 +322,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["WBTC-B"] = CollateralValues({ @@ -363,8 +347,6 @@ contract Config { calc_tau: 0, calc_step: 60, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["WBTC-C"] = CollateralValues({ @@ -390,8 +372,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["TUSD-A"] = CollateralValues({ @@ -417,8 +397,6 @@ contract Config { calc_tau: 250 days, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["KNC-A"] = CollateralValues({ @@ -444,8 +422,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["ZRX-A"] = CollateralValues({ @@ -471,8 +447,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["MANA-A"] = CollateralValues({ @@ -498,8 +472,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["USDT-A"] = CollateralValues({ @@ -525,8 +497,6 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["PAXUSD-A"] = CollateralValues({ @@ -552,8 +522,6 @@ contract Config { calc_tau: 4_320_000, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["COMP-A"] = CollateralValues({ @@ -579,8 +547,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["LRC-A"] = CollateralValues({ @@ -606,8 +572,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["LINK-A"] = CollateralValues({ @@ -633,8 +597,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["BAL-A"] = CollateralValues({ @@ -660,8 +622,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["YFI-A"] = CollateralValues({ @@ -687,8 +647,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["GUSD-A"] = CollateralValues({ @@ -714,8 +672,6 @@ contract Config { calc_tau: 4_320_000, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNI-A"] = CollateralValues({ @@ -741,8 +697,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["RENBTC-A"] = CollateralValues({ @@ -768,8 +722,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["AAVE-A"] = CollateralValues({ @@ -795,8 +747,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNIV2DAIETH-A"] = CollateralValues({ @@ -822,8 +772,6 @@ contract Config { calc_tau: 0, calc_step: 125, calc_cut: 9950, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["PSM-USDC-A"] = CollateralValues({ @@ -849,8 +797,6 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["LITE-PSM-USDC-A"] = CollateralValues({ @@ -876,8 +822,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["UNIV2WBTCETH-A"] = CollateralValues({ @@ -903,8 +847,6 @@ contract Config { calc_tau: 0, calc_step: 130, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNIV2USDCETH-A"] = CollateralValues({ @@ -930,8 +872,6 @@ contract Config { calc_tau: 0, calc_step: 125, calc_cut: 9950, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNIV2DAIUSDC-A"] = CollateralValues({ @@ -957,8 +897,6 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["UNIV2ETHUSDT-A"] = CollateralValues({ @@ -984,8 +922,6 @@ contract Config { calc_tau: 0, calc_step: 125, calc_cut: 9950, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["UNIV2LINKETH-A"] = CollateralValues({ @@ -1011,8 +947,6 @@ contract Config { calc_tau: 0, calc_step: 130, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNIV2UNIETH-A"] = CollateralValues({ @@ -1038,8 +972,6 @@ contract Config { calc_tau: 0, calc_step: 130, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["UNIV2WBTCDAI-A"] = CollateralValues({ @@ -1065,8 +997,6 @@ contract Config { calc_tau: 0, calc_step: 125, calc_cut: 9950, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNIV2AAVEETH-A"] = CollateralValues({ @@ -1092,8 +1022,6 @@ contract Config { calc_tau: 0, calc_step: 130, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["UNIV2DAIUSDT-A"] = CollateralValues({ @@ -1119,8 +1047,6 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA001-A"] = CollateralValues({ @@ -1146,8 +1072,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA002-A"] = CollateralValues({ @@ -1173,8 +1097,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA003-A"] = CollateralValues({ @@ -1200,8 +1122,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA004-A"] = CollateralValues({ @@ -1227,8 +1147,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA005-A"] = CollateralValues({ @@ -1254,8 +1172,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA006-A"] = CollateralValues({ @@ -1281,8 +1197,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA007-A"] = CollateralValues({ @@ -1308,8 +1222,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA008-A"] = CollateralValues({ @@ -1335,8 +1247,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA009-A"] = CollateralValues({ @@ -1362,8 +1272,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA010-A"] = CollateralValues({ @@ -1389,8 +1297,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA011-A"] = CollateralValues({ @@ -1416,8 +1322,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA012-A"] = CollateralValues({ @@ -1443,8 +1347,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA013-A"] = CollateralValues({ @@ -1470,8 +1372,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA014-A"] = CollateralValues({ @@ -1497,8 +1397,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RWA015-A"] = CollateralValues({ @@ -1524,8 +1422,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["MATIC-A"] = CollateralValues({ @@ -1551,8 +1447,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["PSM-PAX-A"] = CollateralValues({ @@ -1578,8 +1472,6 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["GUNIV3DAIUSDC1-A"] = CollateralValues({ @@ -1605,8 +1497,6 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["WSTETH-A"] = CollateralValues({ @@ -1632,8 +1522,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["WSTETH-B"] = CollateralValues({ @@ -1659,8 +1547,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["DIRECT-SPK-AAVE-LIDO-USDS"] = CollateralValues({ @@ -1686,8 +1572,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["DIRECT-AAVEV2-DAI"] = CollateralValues({ @@ -1713,8 +1597,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["DIRECT-COMPV2-DAI"] = CollateralValues({ @@ -1740,8 +1622,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["PSM-GUSD-A"] = CollateralValues({ @@ -1767,8 +1647,6 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["GUNIV3DAIUSDC2-A"] = CollateralValues({ @@ -1794,8 +1672,6 @@ contract Config { calc_tau: 0, calc_step: 120, calc_cut: 9990, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["CRVV1ETHSTETH-A"] = CollateralValues({ @@ -1821,8 +1697,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 9900, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["TELEPORT-FW-A"] = CollateralValues({ @@ -1848,8 +1722,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["RETH-A"] = CollateralValues({ @@ -1875,8 +1747,6 @@ contract Config { calc_tau: 0, calc_step: 90, calc_cut: 99_00, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: true }); afterSpell.collaterals["GNO-A"] = CollateralValues({ @@ -1902,8 +1772,6 @@ contract Config { calc_tau: 0, calc_step: 60, calc_cut: 99_00, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["DIRECT-SPARK-DAI"] = CollateralValues({ @@ -1929,8 +1797,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["DIRECT-SPARK-MORPHO-DAI"] = CollateralValues({ @@ -1956,8 +1822,6 @@ contract Config { calc_tau: 0, calc_step: 0, calc_cut: 0, - engine_fee: 0, - engine_farms: new bytes32[](0), offboarding: false }); afterSpell.collaterals["LSE-MKR-A"] = CollateralValues({ @@ -1983,10 +1847,7 @@ contract Config { calc_tau: 0, calc_step: 60, calc_cut: 99_00, - engine_fee: 5_00, - engine_farms: new bytes32[](1), offboarding: false }); - afterSpell.collaterals["LSE-MKR-A"].engine_farms[0] = "REWARDS_LSMKR_USDS"; } } From 6739b29314bf63251fa19c39ad4be10efbf92e9b Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Wed, 16 Oct 2024 14:23:44 +0200 Subject: [PATCH 26/44] add more end-to-end lockstake tests --- src/DssSpell.t.base.sol | 55 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 886c626d8..b4273caa3 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -231,6 +231,7 @@ interface LitePsmMomLike is AuthorityLike { interface StakingRewardsLike { function rewardsDistribution() external view returns (address); function rewardsDuration() external view returns (uint256); + function rewardsToken() external view returns (address); } interface LockstakeEngineLike { @@ -1340,16 +1341,58 @@ contract DssSpellTestBase is Config, DssTest { _giveTokens(address(sky), lockAmt * afterSpell.sky_mkr_rate); } uint256 snapshot = vm.snapshot(); + // Check locking and freeing Mkr + { + engine.open(0); + assertEq(mkr.balanceOf(address(this)), lockAmt, "LockstakeTake/LockAndFreeMkr/invalid-initial-balance"); + mkr.approve(address(engine), lockAmt); + engine.lock(address(this), 0, lockAmt, 0); + assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/LockAndFreeMkr/invalid-locked-mkr-balance"); + engine.free(address(this), 0, address(this), lockAmt); + uint256 exitFee = lockAmt * p.fee / 10_000; + assertEq(mkr.balanceOf(address(this)), lockAmt - exitFee, "LockstakeTake/LockAndFreeMkr/invalid-unlocked-balance"); + vm.revertTo(snapshot); + } + // Check locking and freeing Sky + { + engine.open(0); + uint256 skyAmt = lockAmt * afterSpell.sky_mkr_rate; + assertEq(sky.balanceOf(address(this)), skyAmt, "LockstakeTake/LockAndFreeSky/invalid-initial-balance"); + sky.approve(address(engine), skyAmt); + engine.lockSky(address(this), 0, skyAmt, 0); + assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/LockAndFreeSky/invalid-locked-mkr-balance"); + engine.freeSky(address(this), 0, address(this), skyAmt); + uint256 exitFee = skyAmt * p.fee / 10_000; + assertEq(sky.balanceOf(address(this)), skyAmt - exitFee, "LockstakeTake/LockAndFreeSky/invalid-unlocked-balance"); + vm.revertTo(snapshot); + } + // Check drawing and wiping + { + address urn = engine.open(0); + assertEq(mkr.balanceOf(address(this)), lockAmt, "LockstakeTake/DrawAndWipe/invalid-initial-balance"); + mkr.approve(address(engine), lockAmt); + engine.lock(address(this), 0, lockAmt, 0); + assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/DrawAndWipe/invalid-locked-mkr-balance"); + engine.draw(address(this), 0, address(this), drawAmt); + assertEq(usds.balanceOf(address(this)), drawAmt, "LockstakeTake/DrawAndWipe/invalid-usds-balance-after-draw"); + skip(1000 days); + jug.drip(p.ilk); + (, uint256 art) = vat.urns(p.ilk, urn); + (, uint256 rate,,,) = vat.ilks(p.ilk); + uint256 wipeAmt = _divup(art * rate, RAY); + assertGt(wipeAmt, drawAmt, "LockstakeTake/DrawAndWipe/invalid-wipe-after-draw"); + _giveTokens(address(usds), wipeAmt); + usds.approve(address(engine), wipeAmt); + engine.wipe(address(this), 0, wipeAmt); + assertEq(usds.balanceOf(address(this)), 0, "LockstakeTake/DrawAndWipe/invalid-usds-balance-after-wipe"); + vm.revertTo(snapshot); + } + // TODO: Check farming and getting a reward + // Check liquidations _checkLockstakeTake(p, lockAmt, drawAmt, false, false); vm.revertTo(snapshot); _checkLockstakeTake(p, lockAmt, drawAmt, false, true); vm.revertTo(snapshot); _checkLockstakeTake(p, lockAmt, drawAmt, true, false); vm.revertTo(snapshot); _checkLockstakeTake(p, lockAmt, drawAmt, true, true); vm.revertTo(snapshot); - - // TODO: add more coverange - // - lock and free mkr and sky - // - draw and wipe - // - farm and get a reward - // - Liquidate with farming and delegating } struct Sale { From bf353a7042adf8d7fe829bd03b267fd76a6ff86b Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Wed, 16 Oct 2024 15:08:06 +0200 Subject: [PATCH 27/44] remove LockstakeIlkParams.gem --- src/DssSpell.t.base.sol | 46 ++++++++++++++++++++--------------------- src/DssSpell.t.sol | 3 +-- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index b4273caa3..7da77d296 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -1237,8 +1237,7 @@ contract DssSpellTestBase is Config, DssTest { struct LockstakeIlkParams { bytes32 ilk; address pip; - address gem; - address lsgem; + address lsmkr; address engine; address clip; address calc; @@ -1252,7 +1251,6 @@ contract DssSpellTestBase is Config, DssTest { LockstakeEngineLike engine = LockstakeEngineLike(p.engine); // Check relevant contracts are correctly configured { - assertEq(p.gem, address(mkr), "LockstakeIlkIntegration/invalid-gem"); assertEq(engine.fee(), p.fee * WAD / 10_000, "LockstakeIlkIntegration/invalid-fee"); assertNotEq(p.farm, address(0), "LockstakeIlkIntegration/invalid-farm"); assertEq(engine.farms(p.farm), 1, "LockstakeIlkIntegration/disabled-farm"); @@ -1264,7 +1262,7 @@ contract DssSpellTestBase is Config, DssTest { assertEq(engine.usdsJoin(), address(usdsJoin), "LockstakeIlkIntegration/invalid-engine-usdsJoin"); assertEq(engine.ilk(), p.ilk, "LockstakeIlkIntegration/invalid-engine-ilk"); assertEq(engine.mkrSky(), address(mkrSky), "LockstakeIlkIntegration/invalid-engine-mkrSky"); - assertEq(engine.lsmkr(), p.lsgem, "LockstakeIlkIntegration/invalid-engine-lsmkr"); + assertEq(engine.lsmkr(), p.lsmkr, "LockstakeIlkIntegration/invalid-engine-lsmkr"); assertEq(engine.jug(), address(jug), "LockstakeIlkIntegration/invalid-engine-jug"); assertEq(ClipAbstract(p.clip).vat(), address(vat), "LockstakeIlkIntegration/invalid-clip-vat"); assertEq(ClipAbstract(p.clip).spotter(), address(spotter), "LockstakeIlkIntegration/invalid-clip-spotter"); @@ -1288,11 +1286,11 @@ contract DssSpellTestBase is Config, DssTest { address gemJoin, address clip ) = reg.info(p.ilk); - assertEq(name, GemAbstract(p.lsgem).name(), "LockstakeIlkIntegration/incorrect-reg-name"); - assertEq(symbol, GemAbstract(p.lsgem).symbol(), "LockstakeIlkIntegration/incorrect-reg-symbol"); + assertEq(name, GemAbstract(p.lsmkr).name(), "LockstakeIlkIntegration/incorrect-reg-name"); + assertEq(symbol, GemAbstract(p.lsmkr).symbol(), "LockstakeIlkIntegration/incorrect-reg-symbol"); assertEq(_class, 7, "LockstakeIlkIntegration/incorrect-reg-class"); // REG_CLASS_JOINLESS - assertEq(decimals, GemAbstract(p.lsgem).decimals(), "LockstakeIlkIntegration/incorrect-reg-dec"); - assertEq(gem, p.gem, "LockstakeIlkIntegration/incorrect-reg-gem"); + assertEq(decimals, GemAbstract(p.lsmkr).decimals(), "LockstakeIlkIntegration/incorrect-reg-dec"); + assertEq(gem, address(mkr), "LockstakeIlkIntegration/incorrect-reg-gem"); assertEq(pip, p.pip, "LockstakeIlkIntegration/incorrect-reg-pip"); assertEq(gemJoin, address(0), "LockstakeIlkIntegration/incorrect-reg-gemJoin"); assertEq(clip, p.clip, "LockstakeIlkIntegration/incorrect-reg-xlip"); @@ -1303,7 +1301,7 @@ contract DssSpellTestBase is Config, DssTest { assertEq(vat.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-vat-clip"); assertEq(WardsAbstract(p.pip).wards(address(osmMom)), 1, "LockstakeIlkIntegration/missing-auth-pip-osmMom"); assertEq(dog.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-dog-clip"); - assertEq(WardsAbstract(p.lsgem).wards(p.engine), 1, "LockstakeIlkIntegration/missing-auth-lsgem-engine"); + assertEq(WardsAbstract(p.lsmkr).wards(p.engine), 1, "LockstakeIlkIntegration/missing-auth-lsgem-engine"); assertEq(WardsAbstract(p.engine).wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-engine-clip"); assertEq(WardsAbstract(p.clip).wards(address(dog)), 1, "LockstakeIlkIntegration/missing-auth-clip-dog"); assertEq(WardsAbstract(p.clip).wards(address(end)), 1, "LockstakeIlkIntegration/missing-auth-clip-end"); @@ -1425,24 +1423,24 @@ contract DssSpellTestBase is Config, DssTest { if (withStaking) { engine.selectFarm(address(this), 0, address(p.farm), 0); } - uint256 previousCheifBalance = GemAbstract(p.gem).balanceOf(address(chief)); - GemAbstract(p.gem).approve(address(engine), lockAmt); + uint256 previousCheifBalance = mkr.balanceOf(address(chief)); + mkr.approve(address(engine), lockAmt); engine.lock(address(this), 0, lockAmt, 0); engine.draw(address(this), 0, address(this), drawAmt); if (withDelegate) { assertEq(engine.urnVoteDelegates(urn), voteDelegate, "LockstakeTake/AfterLockDraw/withDelegate/invalid-voteDelegate-urn"); - assertEq(GemAbstract(p.gem).balanceOf(address(chief)) - previousCheifBalance, lockAmt, "LockstakeTake/AfterLockDraw/withDelegate/invalid-chief-mkr-balance"); - assertEq(GemAbstract(p.gem).balanceOf(p.engine), 0, "LockstakeTake/AfterLockDraw/withDelegate/invalid-engine-balance"); + assertEq(mkr.balanceOf(address(chief)) - previousCheifBalance, lockAmt, "LockstakeTake/AfterLockDraw/withDelegate/invalid-chief-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), 0, "LockstakeTake/AfterLockDraw/withDelegate/invalid-engine-balance"); } else { assertEq(engine.urnVoteDelegates(urn), address(0), "LockstakeTake/AfterLockDraw/withoutDelegate/invalid-voteDelegate-urn"); - assertEq(GemAbstract(p.gem).balanceOf(p.engine), lockAmt, "LockstakeTake/AfterLockDraw/withoutDelegate/invalid-engine-balance"); + assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/AfterLockDraw/withoutDelegate/invalid-engine-balance"); } if (withStaking) { - assertEq(GemAbstract(p.lsgem).balanceOf(urn), 0, "LockstakeTake/AfterLockDraw/withStaking/invalid-urn-lsgem-balance"); - assertEq(GemAbstract(p.lsgem).balanceOf(p.farm), lockAmt, "LockstakeTake/AfterLockDraw/withStaking/invalid-farm-lsgem-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(urn), 0, "LockstakeTake/AfterLockDraw/withStaking/invalid-urn-lsgem-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), lockAmt, "LockstakeTake/AfterLockDraw/withStaking/invalid-farm-lsgem-balance"); assertEq(GemAbstract(p.farm).balanceOf(urn), lockAmt, "LockstakeTake/AfterLockDraw/withStaking/invalid-urn-farm-balance"); } else { - assertEq(GemAbstract(p.lsgem).balanceOf(urn), lockAmt, "LockstakeTake/AfterLockDraw/withoutStaking/invalid-urn-lsgem-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(urn), lockAmt, "LockstakeTake/AfterLockDraw/withoutStaking/invalid-urn-lsgem-balance"); } // Force liquidation @@ -1466,13 +1464,13 @@ contract DssSpellTestBase is Config, DssTest { assertEq(sale.tic, block.timestamp, "LockstakeTake/AfterBark/invalid-sale.tic"); assertEq(sale.top, _getOSMPrice(p.pip) * ClipAbstract(p.clip).buf() / WAD, "LockstakeTake/AfterBark/invalid-sale.top"); assertEq(vat.gem(p.ilk, p.clip), lockAmt, "LockstakeTake/AfterBark/invalid-vat-gem-clip"); - assertEq(GemAbstract(p.gem).balanceOf(p.engine), lockAmt, "LockstakeTake/AfterBark/invalid-engine-mkr-balance"); - assertEq(GemAbstract(p.lsgem).balanceOf(urn), 0, "LockstakeTake/AfterBark/invalid-urn-lsgem-balance"); + assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/AfterBark/invalid-engine-mkr-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(urn), 0, "LockstakeTake/AfterBark/invalid-urn-lsgem-balance"); if (withDelegate) { - assertEq(GemAbstract(p.gem).balanceOf(voteDelegate), 0, "LockstakeTake/AfterBark/withDelegate/invalid-voteDelegate-mkr-balance"); + assertEq(mkr.balanceOf(voteDelegate), 0, "LockstakeTake/AfterBark/withDelegate/invalid-voteDelegate-mkr-balance"); } if (withStaking) { - assertEq(GemAbstract(p.lsgem).balanceOf(p.farm), 0, "LockstakeTake/AfterBark/withStaking/invalid-farm-lsgem-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), 0, "LockstakeTake/AfterBark/withStaking/invalid-farm-lsgem-balance"); assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "LockstakeTake/AfterBark/withStaking/invalid-urn-farm-balance"); } @@ -1480,9 +1478,9 @@ contract DssSpellTestBase is Config, DssTest { address buyer = address(888); vm.prank(pauseProxy); vat.suck(address(0), buyer, sale.tab); vm.prank(buyer); vat.hope(p.clip); - assertEq(GemAbstract(p.gem).balanceOf(buyer), 0, "LockstakeTake/AfterBark/invalid-buyer-mkr-balance"); + assertEq(mkr.balanceOf(buyer), 0, "LockstakeTake/AfterBark/invalid-buyer-mkr-balance"); vm.prank(buyer); ClipAbstract(p.clip).take(id, lockAmt, type(uint256).max, buyer, ""); - assertGt(GemAbstract(p.gem).balanceOf(buyer), 0, "LockstakeTake/AfterTake/invalid-buyer-mkr-balance"); + assertGt(mkr.balanceOf(buyer), 0, "LockstakeTake/AfterTake/invalid-buyer-mkr-balance"); (sale.pos, sale.tab, sale.lot, sale.tot, sale.usr, sale.tic, sale.top) = LockstakeClipperLike(p.clip).sales(id); assertEq(sale.pos, 0, "LockstakeTake/AfterTake/invalid-sale.pos"); assertEq(sale.tab, 0, "LockstakeTake/AfterTake/invalid-sale.tab"); @@ -1493,7 +1491,7 @@ contract DssSpellTestBase is Config, DssTest { assertEq(sale.top, 0, "LockstakeTake/AfterTake/invalid-sale.top"); assertEq(vat.gem(p.ilk, p.clip), 0, "LockstakeTake/AfterTake/invalid-vat.gem"); if (withStaking) { - assertEq(GemAbstract(p.lsgem).balanceOf(p.farm), 0, "LockstakeTake/AfterTake/withStaking/invalid-lsgem-farm-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), 0, "LockstakeTake/AfterTake/withStaking/invalid-lsgem-farm-balance"); assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "LockstakeTake/AfterTake/withStaking/invalid-farm-urn-balance"); } } diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index bb27eb058..8567ca9b3 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -290,8 +290,7 @@ contract DssSpellTest is DssSpellTestBase { LockstakeIlkParams({ ilk: "LSE-MKR-A", pip: addr.addr("PIP_MKR"), - gem: addr.addr("MCD_GOV"), - lsgem: addr.addr("LOCKSTAKE_MKR"), + lsmkr: addr.addr("LOCKSTAKE_MKR"), engine: addr.addr("LOCKSTAKE_ENGINE"), clip: addr.addr("LOCKSTAKE_CLIP"), calc: addr.addr("LOCKSTAKE_CLIP_CALC"), From cc8e3475038d087d2ccc29ea029bc74d6d95e878 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Wed, 16 Oct 2024 15:18:53 +0200 Subject: [PATCH 28/44] cleanup checkLockstakeIlkIntegration --- src/DssSpell.t.base.sol | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 7da77d296..84bd4d0aa 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -1297,22 +1297,22 @@ contract DssSpellTestBase is Config, DssTest { } // Check required authorizations { - assertEq(vat.wards(p.engine), 1, "LockstakeIlkIntegration/missing-auth-vat-engine"); - assertEq(vat.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-vat-clip"); - assertEq(WardsAbstract(p.pip).wards(address(osmMom)), 1, "LockstakeIlkIntegration/missing-auth-pip-osmMom"); - assertEq(dog.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-dog-clip"); - assertEq(WardsAbstract(p.lsmkr).wards(p.engine), 1, "LockstakeIlkIntegration/missing-auth-lsgem-engine"); - assertEq(WardsAbstract(p.engine).wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-engine-clip"); - assertEq(WardsAbstract(p.clip).wards(address(dog)), 1, "LockstakeIlkIntegration/missing-auth-clip-dog"); - assertEq(WardsAbstract(p.clip).wards(address(end)), 1, "LockstakeIlkIntegration/missing-auth-clip-end"); - assertEq(WardsAbstract(p.clip).wards(address(clipMom)), 1, "LockstakeIlkIntegration/missing-auth-clip-clipMom"); + assertEq(vat.wards(p.engine), 1, "LockstakeIlkIntegration/missing-auth-vat-engine"); + assertEq(vat.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-vat-clip"); + assertEq(WardsAbstract(p.pip).wards(address(osmMom)), 1, "LockstakeIlkIntegration/missing-auth-pip-osmMom"); + assertEq(dog.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-dog-clip"); + assertEq(WardsAbstract(p.lsmkr).wards(p.engine), 1, "LockstakeIlkIntegration/missing-auth-lsgem-engine"); + assertEq(WardsAbstract(p.engine).wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-engine-clip"); + assertEq(WardsAbstract(p.clip).wards(address(dog)), 1, "LockstakeIlkIntegration/missing-auth-clip-dog"); + assertEq(WardsAbstract(p.clip).wards(address(end)), 1, "LockstakeIlkIntegration/missing-auth-clip-end"); + assertEq(WardsAbstract(p.clip).wards(address(clipMom)), 1, "LockstakeIlkIntegration/missing-auth-clip-clipMom"); } // Check required OSM buds { - assertEq(OsmAbstract(p.pip).bud(address(spotter)), 1, "LockstakeIlkIntegration/missing-spotter-bud"); - assertEq(OsmAbstract(p.pip).bud(p.clip), 1, "LockstakeIlkIntegration/missing-clip-bud"); - assertEq(OsmAbstract(p.pip).bud(address(clipMom)), 1, "LockstakeIlkIntegration/missing-clipMom-bud"); - assertEq(OsmAbstract(p.pip).bud(address(end)), 1, "LockstakeIlkIntegration/missing-end-bud"); + assertEq(OsmAbstract(p.pip).bud(address(spotter)), 1, "LockstakeIlkIntegration/missing-spotter-bud"); + assertEq(OsmAbstract(p.pip).bud(p.clip), 1, "LockstakeIlkIntegration/missing-clip-bud"); + assertEq(OsmAbstract(p.pip).bud(address(clipMom)), 1, "LockstakeIlkIntegration/missing-clipMom-bud"); + assertEq(OsmAbstract(p.pip).bud(address(end)), 1, "LockstakeIlkIntegration/missing-end-bud"); } // Prepare for liquidation uint256 drawAmt; @@ -1333,7 +1333,7 @@ contract DssSpellTestBase is Config, DssTest { // Calculate lock and draw amounts (,,,, uint256 dust) = vat.ilks(p.ilk); drawAmt = dust / RAY; - lockAmt = drawAmt / 2; + lockAmt = drawAmt * WAD / _getOSMPrice(p.pip) * 10; // Give tokens _giveTokens(address(mkr), lockAmt); _giveTokens(address(sky), lockAmt * afterSpell.sky_mkr_rate); @@ -1373,8 +1373,6 @@ contract DssSpellTestBase is Config, DssTest { assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/DrawAndWipe/invalid-locked-mkr-balance"); engine.draw(address(this), 0, address(this), drawAmt); assertEq(usds.balanceOf(address(this)), drawAmt, "LockstakeTake/DrawAndWipe/invalid-usds-balance-after-draw"); - skip(1000 days); - jug.drip(p.ilk); (, uint256 art) = vat.urns(p.ilk, urn); (, uint256 rate,,,) = vat.ilks(p.ilk); uint256 wipeAmt = _divup(art * rate, RAY); From a311fb0900bd5407b818c7ddb5860224258aeaca Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Wed, 16 Oct 2024 16:26:56 +0200 Subject: [PATCH 29/44] check farm parameters --- src/DssSpell.t.base.sol | 15 +++++++++++---- src/DssSpell.t.sol | 5 ++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 84bd4d0aa..065a9fac9 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -1236,13 +1236,16 @@ contract DssSpellTestBase is Config, DssTest { struct LockstakeIlkParams { bytes32 ilk; + uint256 fee; address pip; address lsmkr; address engine; address clip; address calc; address farm; - uint256 fee; + address rToken; + address rDistr; + uint256 rDur; } function _checkLockstakeIlkIntegration( @@ -1251,9 +1254,7 @@ contract DssSpellTestBase is Config, DssTest { LockstakeEngineLike engine = LockstakeEngineLike(p.engine); // Check relevant contracts are correctly configured { - assertEq(engine.fee(), p.fee * WAD / 10_000, "LockstakeIlkIntegration/invalid-fee"); - assertNotEq(p.farm, address(0), "LockstakeIlkIntegration/invalid-farm"); - assertEq(engine.farms(p.farm), 1, "LockstakeIlkIntegration/disabled-farm"); + StakingRewardsLike farm = StakingRewardsLike(p.farm); assertEq(dog.vat(), address(vat), "LockstakeIlkIntegration/invalid-dog-vat"); assertEq(dog.vow(), address(vow), "LockstakeIlkIntegration/invalid-dog-vow"); (address clip,,,) = dog.ilks(p.ilk); @@ -1264,6 +1265,12 @@ contract DssSpellTestBase is Config, DssTest { assertEq(engine.mkrSky(), address(mkrSky), "LockstakeIlkIntegration/invalid-engine-mkrSky"); assertEq(engine.lsmkr(), p.lsmkr, "LockstakeIlkIntegration/invalid-engine-lsmkr"); assertEq(engine.jug(), address(jug), "LockstakeIlkIntegration/invalid-engine-jug"); + assertEq(engine.fee(), p.fee * WAD / 10_000, "LockstakeIlkIntegration/invalid-fee"); + assertNotEq(p.farm, address(0), "LockstakeIlkIntegration/invalid-farm"); + assertEq(engine.farms(p.farm), 1, "LockstakeIlkIntegration/disabled-farm"); + assertEq(farm.rewardsToken(), p.rToken, "LockstakeIlkIntegration/invalid-rewardsToken"); + assertEq(farm.rewardsDistribution(), p.rDistr, "LockstakeIlkIntegration/invalid-rewardsDistribution"); + assertEq(farm.rewardsDuration(), p.rDur, "LockstakeIlkIntegration/invalid-rewardsDuration"); assertEq(ClipAbstract(p.clip).vat(), address(vat), "LockstakeIlkIntegration/invalid-clip-vat"); assertEq(ClipAbstract(p.clip).spotter(), address(spotter), "LockstakeIlkIntegration/invalid-clip-spotter"); assertEq(ClipAbstract(p.clip).dog(), address(dog), "LockstakeIlkIntegration/invalid-clip-dog"); diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index 8567ca9b3..f2b5b1592 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -289,13 +289,16 @@ contract DssSpellTest is DssSpellTestBase { _checkLockstakeIlkIntegration( LockstakeIlkParams({ ilk: "LSE-MKR-A", + fee: 5_00, pip: addr.addr("PIP_MKR"), lsmkr: addr.addr("LOCKSTAKE_MKR"), engine: addr.addr("LOCKSTAKE_ENGINE"), clip: addr.addr("LOCKSTAKE_CLIP"), calc: addr.addr("LOCKSTAKE_CLIP_CALC"), farm: addr.addr("REWARDS_LSMKR_USDS"), - fee: 5_00 + rToken: addr.addr("USDS"), + rDistr: addr.addr("MCD_SPLIT"), + rDur: 15_649 seconds }) ); } From fddf115daa5cbc0922a049cd658188b6b93befbe Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Wed, 16 Oct 2024 17:52:40 +0200 Subject: [PATCH 30/44] test lockstake farming and getting a reward --- src/DssSpell.t.base.sol | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 065a9fac9..9ea797246 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -229,9 +229,13 @@ interface LitePsmMomLike is AuthorityLike { } interface StakingRewardsLike { + function stake(uint256 amount) external; + function notifyRewardAmount(uint256 reward) external; + function rewardPerToken() external view returns (uint256); function rewardsDistribution() external view returns (address); function rewardsDuration() external view returns (uint256); function rewardsToken() external view returns (address); + function stakingToken() external view returns (address); } interface LockstakeEngineLike { @@ -1252,9 +1256,9 @@ contract DssSpellTestBase is Config, DssTest { LockstakeIlkParams memory p ) internal { LockstakeEngineLike engine = LockstakeEngineLike(p.engine); + StakingRewardsLike farm = StakingRewardsLike(p.farm); // Check relevant contracts are correctly configured { - StakingRewardsLike farm = StakingRewardsLike(p.farm); assertEq(dog.vat(), address(vat), "LockstakeIlkIntegration/invalid-dog-vat"); assertEq(dog.vow(), address(vow), "LockstakeIlkIntegration/invalid-dog-vow"); (address clip,,,) = dog.ilks(p.ilk); @@ -1268,6 +1272,7 @@ contract DssSpellTestBase is Config, DssTest { assertEq(engine.fee(), p.fee * WAD / 10_000, "LockstakeIlkIntegration/invalid-fee"); assertNotEq(p.farm, address(0), "LockstakeIlkIntegration/invalid-farm"); assertEq(engine.farms(p.farm), 1, "LockstakeIlkIntegration/disabled-farm"); + assertEq(farm.stakingToken(), p.lsmkr, "LockstakeIlkIntegration/invalid-stakingToken"); assertEq(farm.rewardsToken(), p.rToken, "LockstakeIlkIntegration/invalid-rewardsToken"); assertEq(farm.rewardsDistribution(), p.rDistr, "LockstakeIlkIntegration/invalid-rewardsDistribution"); assertEq(farm.rewardsDuration(), p.rDur, "LockstakeIlkIntegration/invalid-rewardsDuration"); @@ -1390,7 +1395,27 @@ contract DssSpellTestBase is Config, DssTest { assertEq(usds.balanceOf(address(this)), 0, "LockstakeTake/DrawAndWipe/invalid-usds-balance-after-wipe"); vm.revertTo(snapshot); } - // TODO: Check farming and getting a reward + // Check farming and getting a reward + { + // Lock with selected farm + address urn = engine.open(0); + mkr.approve(address(engine), lockAmt); + engine.selectFarm(address(this), 0, p.farm, 0); + engine.lock(address(this), 0, lockAmt, 0); + assertEq(GemAbstract(p.farm).balanceOf(urn), lockAmt, "LockstakeTake/FarmAndGetReward/FarmAndGetReward/invalid-urn-farm-balance"); + // Deposit rewards into farm and notify + uint256 rewardAmt = 1_000_000 * WAD; + address rewardsToken = farm.rewardsToken(); + deal(rewardsToken, p.farm, rewardAmt, true); + vm.prank(farm.rewardsDistribution()); farm.notifyRewardAmount(rewardAmt); + // Claim rewards + address rewardsUser = address(this); + skip(farm.rewardsDuration()); + uint256 resultAmt = engine.getReward(address(this), 0, p.farm, rewardsUser); + assertGt(resultAmt, 0, "LockstakeTake/FarmAndGetReward/no-reward-amt"); + assertGt(GemAbstract(rewardsToken).balanceOf(rewardsUser), 0, "LockstakeTake/FarmAndGetReward/no-reward-balance"); + vm.revertTo(snapshot); + } // Check liquidations _checkLockstakeTake(p, lockAmt, drawAmt, false, false); vm.revertTo(snapshot); _checkLockstakeTake(p, lockAmt, drawAmt, false, true); vm.revertTo(snapshot); From 625a240370b3e78fedf531de3b2dd307995d8eed Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Wed, 16 Oct 2024 17:58:51 +0200 Subject: [PATCH 31/44] update comment above chainlog version bump --- src/DssSpell.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index 1b0ec225f..a2a666429 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -400,7 +400,7 @@ contract DssSpellAction is DssAction { // ---------- Chainlog bump ---------- - // Note: we have to increase minor chainlog version as new keys are added + // Note: we have to patch chainlog version as new collateral is added DssExecLib.setChangelogVersion("1.19.2"); } } From 94b6ff9e9be32cd6ef08c27b40150fe0a1d09007 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Wed, 16 Oct 2024 23:25:32 +0200 Subject: [PATCH 32/44] address nits and typos --- src/DssSpell.t.base.sol | 6 +++--- src/DssSpell.t.sol | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 9ea797246..6a81b2ed1 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -1269,7 +1269,7 @@ contract DssSpellTestBase is Config, DssTest { assertEq(engine.mkrSky(), address(mkrSky), "LockstakeIlkIntegration/invalid-engine-mkrSky"); assertEq(engine.lsmkr(), p.lsmkr, "LockstakeIlkIntegration/invalid-engine-lsmkr"); assertEq(engine.jug(), address(jug), "LockstakeIlkIntegration/invalid-engine-jug"); - assertEq(engine.fee(), p.fee * WAD / 10_000, "LockstakeIlkIntegration/invalid-fee"); + assertEq(engine.fee(), p.fee * WAD / 100_00, "LockstakeIlkIntegration/invalid-fee"); assertNotEq(p.farm, address(0), "LockstakeIlkIntegration/invalid-farm"); assertEq(engine.farms(p.farm), 1, "LockstakeIlkIntegration/disabled-farm"); assertEq(farm.stakingToken(), p.lsmkr, "LockstakeIlkIntegration/invalid-stakingToken"); @@ -1359,7 +1359,7 @@ contract DssSpellTestBase is Config, DssTest { engine.lock(address(this), 0, lockAmt, 0); assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/LockAndFreeMkr/invalid-locked-mkr-balance"); engine.free(address(this), 0, address(this), lockAmt); - uint256 exitFee = lockAmt * p.fee / 10_000; + uint256 exitFee = lockAmt * p.fee / 100_00; assertEq(mkr.balanceOf(address(this)), lockAmt - exitFee, "LockstakeTake/LockAndFreeMkr/invalid-unlocked-balance"); vm.revertTo(snapshot); } @@ -1372,7 +1372,7 @@ contract DssSpellTestBase is Config, DssTest { engine.lockSky(address(this), 0, skyAmt, 0); assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/LockAndFreeSky/invalid-locked-mkr-balance"); engine.freeSky(address(this), 0, address(this), skyAmt); - uint256 exitFee = skyAmt * p.fee / 10_000; + uint256 exitFee = skyAmt * p.fee / 100_00; assertEq(sky.balanceOf(address(this)), skyAmt - exitFee, "LockstakeTake/LockAndFreeSky/invalid-unlocked-balance"); vm.revertTo(snapshot); } diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index f2b5b1592..4b41e6b7d 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -378,15 +378,15 @@ contract DssSpellTest is DssSpellTestBase { function testNewAuthorizations() public { // add the `skipped` modifier to skip Authorization[9] memory newAuthorizations = [ - Authorization({ base: "MCD_VAT", ward: "LOCKSTAKE_ENGINE" }), - Authorization({ base: "MCD_VAT", ward: "LOCKSTAKE_CLIP" }), - Authorization({ base: "PIP_MKR", ward: "OSM_MOM" }), - Authorization({ base: "MCD_DOG", ward: "LOCKSTAKE_CLIP" }), - Authorization({ base: "LOCKSTAKE_MKR", ward: "LOCKSTAKE_ENGINE" }), - Authorization({ base: "LOCKSTAKE_ENGINE", ward: "LOCKSTAKE_CLIP" }), - Authorization({ base: "LOCKSTAKE_CLIP", ward: "MCD_DOG" }), - Authorization({ base: "LOCKSTAKE_CLIP", ward: "MCD_END" }), - Authorization({ base: "LOCKSTAKE_CLIP", ward: "CLIPPER_MOM" }) + Authorization({ base: "MCD_VAT", ward: "LOCKSTAKE_ENGINE" }), + Authorization({ base: "MCD_VAT", ward: "LOCKSTAKE_CLIP" }), + Authorization({ base: "PIP_MKR", ward: "OSM_MOM" }), + Authorization({ base: "MCD_DOG", ward: "LOCKSTAKE_CLIP" }), + Authorization({ base: "LOCKSTAKE_MKR", ward: "LOCKSTAKE_ENGINE" }), + Authorization({ base: "LOCKSTAKE_ENGINE", ward: "LOCKSTAKE_CLIP" }), + Authorization({ base: "LOCKSTAKE_CLIP", ward: "MCD_DOG" }), + Authorization({ base: "LOCKSTAKE_CLIP", ward: "MCD_END" }), + Authorization({ base: "LOCKSTAKE_CLIP", ward: "CLIPPER_MOM" }) ]; for (uint256 i = 0; i < newAuthorizations.length; i++) { @@ -678,7 +678,7 @@ contract DssSpellTest is DssSpellTestBase { assertEq( actualTotalDiff.dai + actualTotalDiff.usds, calculatedTotalDiff.dai + calculatedTotalDiff.usds, - "TestPayments/invalid-dai-sky-total" + "TestPayments/invalid-dai-usds-total" ); assertEq( actualTotalDiff.mkr * int256(afterSpell.sky_mkr_rate) + actualTotalDiff.sky, From b2a60339468c2fa667212c7aa1447c8f49ea99d4 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Thu, 17 Oct 2024 09:08:12 +0200 Subject: [PATCH 33/44] nit: update contract declaration pattern in config --- src/DssSpell.t.base.sol | 16 ++++++++-------- src/test/config.sol | 30 +++++++++++++++--------------- src/test/starknet.t.sol | 4 ++-- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 6a81b2ed1..2566abe0f 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -511,7 +511,7 @@ contract DssSpellTestBase is Config, DssTest { } function setUp() public { - setValues(address(chief)); + setValues(); _castPreviousSpell(); spellValues.deployed_spell_created = spellValues.deployed_spell != address(0) @@ -673,25 +673,25 @@ contract DssSpellTestBase is Config, DssTest { } // check Pause authority - assertEq(pause.authority(), values.pause_authority, "TestError/pause-authority"); + assertEq(pause.authority(), addr.addr(values.pause_authority), "TestError/pause-authority"); // check OsmMom authority - assertEq(osmMom.authority(), values.osm_mom_authority, "TestError/osmMom-authority"); + assertEq(osmMom.authority(), addr.addr(values.osm_mom_authority), "TestError/osmMom-authority"); // check ClipperMom authority - assertEq(clipMom.authority(), values.clipper_mom_authority, "TestError/clipperMom-authority"); + assertEq(clipMom.authority(), addr.addr(values.clipper_mom_authority), "TestError/clipperMom-authority"); // check D3MMom authority - assertEq(d3mMom.authority(), values.d3m_mom_authority, "TestError/d3mMom-authority"); + assertEq(d3mMom.authority(), addr.addr(values.d3m_mom_authority), "TestError/d3mMom-authority"); // check LineMom authority - assertEq(lineMom.authority(), values.line_mom_authority, "TestError/lineMom-authority"); + assertEq(lineMom.authority(), addr.addr(values.line_mom_authority), "TestError/lineMom-authority"); // check LitePsmMom authority - assertEq(litePsmMom.authority(), values.lite_psm_mom_authority, "TestError/linePsmMom-authority"); + assertEq(litePsmMom.authority(), addr.addr(values.lite_psm_mom_authority), "TestError/linePsmMom-authority"); // check SplitterMom authority - assertEq(splitterMom.authority(), values.splitter_mom_authority, "TestError/splitterMom-authority"); + assertEq(splitterMom.authority(), addr.addr(values.splitter_mom_authority), "TestError/splitterMom-authority"); // check number of ilks assertEq(reg.count(), values.ilk_count, "TestError/ilks-count"); diff --git a/src/test/config.sol b/src/test/config.sol index 889ffb8d4..61257e581 100644 --- a/src/test/config.sol +++ b/src/test/config.sol @@ -44,13 +44,13 @@ contract Config { uint256 flap_want; uint256 dog_Hole; uint256 esm_min; - address pause_authority; - address osm_mom_authority; - address clipper_mom_authority; - address d3m_mom_authority; - address line_mom_authority; - address lite_psm_mom_authority; - address splitter_mom_authority; + bytes32 pause_authority; + bytes32 osm_mom_authority; + bytes32 clipper_mom_authority; + bytes32 d3m_mom_authority; + bytes32 line_mom_authority; + bytes32 lite_psm_mom_authority; + bytes32 splitter_mom_authority; uint256 vest_dai_cap; uint256 vest_mkr_cap; uint256 vest_sky_cap; @@ -94,7 +94,7 @@ contract Config { SpellValues spellValues; SystemValues afterSpell; - function setValues(address chief) public { + function setValues() public { // Add spells if there is a need to test prior to their cast() functions // being called on-chain. They will be executed in order from index 0. address[] memory prevSpells = new address[](0); @@ -131,13 +131,13 @@ contract Config { afterSpell.flap_want = 9800; // In basis points afterSpell.dog_Hole = 150 * MILLION; // In whole Dai units afterSpell.esm_min = 300 * THOUSAND; // In whole MKR units - afterSpell.pause_authority = chief; // Pause authority - afterSpell.osm_mom_authority = chief; // OsmMom authority - afterSpell.clipper_mom_authority = chief; // ClipperMom authority - afterSpell.d3m_mom_authority = chief; // D3MMom authority - afterSpell.line_mom_authority = chief; // LineMom authority - afterSpell.lite_psm_mom_authority = chief; // LitePsmMom authority - afterSpell.splitter_mom_authority = chief; // SplitterMom authority + afterSpell.pause_authority = "MCD_ADM"; // Pause authority + afterSpell.osm_mom_authority = "MCD_ADM"; // OsmMom authority + afterSpell.clipper_mom_authority = "MCD_ADM"; // ClipperMom authority + afterSpell.d3m_mom_authority = "MCD_ADM"; // D3MMom authority + afterSpell.line_mom_authority = "MCD_ADM"; // LineMom authority + afterSpell.lite_psm_mom_authority = "MCD_ADM"; // LitePsmMom authority + afterSpell.splitter_mom_authority = "MCD_ADM"; // SplitterMom authority afterSpell.vest_dai_cap = 1 * MILLION * WAD / 30 days; // In WAD Dai per second afterSpell.vest_mkr_cap = 2_220 * WAD / 365 days; // In WAD MKR per second afterSpell.vest_sky_cap = 800 * MILLION * WAD / 365 days; // In WAD SKY per second diff --git a/src/test/starknet.t.sol b/src/test/starknet.t.sol index eacf71a5b..d35faf420 100644 --- a/src/test/starknet.t.sol +++ b/src/test/starknet.t.sol @@ -32,7 +32,7 @@ contract ConfigStarknet { uint256 relay_selector; } - function setValues() public { + function setStarknetValues() public { uint256 WAD = 10 ** 18; starknetValues = StarknetValues({ @@ -99,7 +99,7 @@ contract StarknetTests is DssSpellTestBase, ConfigStarknet { ); constructor() { - setValues(); + setStarknetValues(); } function testStarknet() public { From dd5fd3aa76c0d3f67ad2e6ff6ea8d0ed3a4b4bcb Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Thu, 17 Oct 2024 09:12:04 +0200 Subject: [PATCH 34/44] use ExecLib address helpers --- src/DssSpell.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index a2a666429..a5d988232 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -79,16 +79,16 @@ contract DssSpellAction is DssAction { // ---------- Contracts ---------- address internal immutable MCD_VAT = DssExecLib.vat(); + address internal immutable MCD_VOW = DssExecLib.vow(); + address internal immutable MCD_GOV = DssExecLib.mkr(); address internal immutable MIP21_LIQUIDATION_ORACLE = DssExecLib.getChangelogAddress("MIP21_LIQUIDATION_ORACLE"); address internal immutable RWA007_A_URN = DssExecLib.getChangelogAddress("RWA007_A_URN"); address internal immutable RWA014_A_URN = DssExecLib.getChangelogAddress("RWA014_A_URN"); address internal immutable PIP_MKR = DssExecLib.getChangelogAddress("PIP_MKR"); address internal immutable VOTE_DELEGATE_PROXY_FACTORY = DssExecLib.getChangelogAddress("VOTE_DELEGATE_PROXY_FACTORY"); address internal immutable MCD_SPLIT = DssExecLib.getChangelogAddress("MCD_SPLIT"); - address internal immutable MCD_VOW = DssExecLib.getChangelogAddress("MCD_VOW"); address internal immutable USDS_JOIN = DssExecLib.getChangelogAddress("USDS_JOIN"); address internal immutable USDS = DssExecLib.getChangelogAddress("USDS"); - address internal immutable MCD_GOV = DssExecLib.getChangelogAddress("MCD_GOV"); address internal immutable MKR_SKY = DssExecLib.getChangelogAddress("MKR_SKY"); address internal immutable SKY = DssExecLib.getChangelogAddress("SKY"); address internal constant NEW_PIP_MKR = 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b; From 7ad8e67a667724f31533ca7a4520fae524bf8f61 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Thu, 17 Oct 2024 10:24:56 +0200 Subject: [PATCH 35/44] cleanup lockstake base test --- src/DssSpell.t.base.sol | 210 ++++++++++++++++++++-------------------- 1 file changed, 107 insertions(+), 103 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 2566abe0f..a438068d0 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -1259,32 +1259,32 @@ contract DssSpellTestBase is Config, DssTest { StakingRewardsLike farm = StakingRewardsLike(p.farm); // Check relevant contracts are correctly configured { - assertEq(dog.vat(), address(vat), "LockstakeIlkIntegration/invalid-dog-vat"); - assertEq(dog.vow(), address(vow), "LockstakeIlkIntegration/invalid-dog-vow"); + assertEq(dog.vat(), address(vat), "checkLockstakeIlkIntegration/invalid-dog-vat"); + assertEq(dog.vow(), address(vow), "checkLockstakeIlkIntegration/invalid-dog-vow"); (address clip,,,) = dog.ilks(p.ilk); - assertEq(clip, p.clip, "LockstakeIlkIntegration/invalid-dog-clip"); - assertEq(engine.voteDelegateFactory(), voteDelegateFactory, "LockstakeIlkIntegration/invalid-engine-voteDelegateFactory"); - assertEq(engine.usdsJoin(), address(usdsJoin), "LockstakeIlkIntegration/invalid-engine-usdsJoin"); - assertEq(engine.ilk(), p.ilk, "LockstakeIlkIntegration/invalid-engine-ilk"); - assertEq(engine.mkrSky(), address(mkrSky), "LockstakeIlkIntegration/invalid-engine-mkrSky"); - assertEq(engine.lsmkr(), p.lsmkr, "LockstakeIlkIntegration/invalid-engine-lsmkr"); - assertEq(engine.jug(), address(jug), "LockstakeIlkIntegration/invalid-engine-jug"); - assertEq(engine.fee(), p.fee * WAD / 100_00, "LockstakeIlkIntegration/invalid-fee"); - assertNotEq(p.farm, address(0), "LockstakeIlkIntegration/invalid-farm"); - assertEq(engine.farms(p.farm), 1, "LockstakeIlkIntegration/disabled-farm"); - assertEq(farm.stakingToken(), p.lsmkr, "LockstakeIlkIntegration/invalid-stakingToken"); - assertEq(farm.rewardsToken(), p.rToken, "LockstakeIlkIntegration/invalid-rewardsToken"); - assertEq(farm.rewardsDistribution(), p.rDistr, "LockstakeIlkIntegration/invalid-rewardsDistribution"); - assertEq(farm.rewardsDuration(), p.rDur, "LockstakeIlkIntegration/invalid-rewardsDuration"); - assertEq(ClipAbstract(p.clip).vat(), address(vat), "LockstakeIlkIntegration/invalid-clip-vat"); - assertEq(ClipAbstract(p.clip).spotter(), address(spotter), "LockstakeIlkIntegration/invalid-clip-spotter"); - assertEq(ClipAbstract(p.clip).dog(), address(dog), "LockstakeIlkIntegration/invalid-clip-dog"); - assertEq(ClipAbstract(p.clip).ilk(), p.ilk, "LockstakeIlkIntegration/invalid-clip-ilk"); - assertEq(ClipAbstract(p.clip).vow(), address(vow), "LockstakeIlkIntegration/invalid-clip-vow"); - assertEq(ClipAbstract(p.clip).calc(), p.calc, "LockstakeIlkIntegration/invalid-clip-calc"); - assertEq(LockstakeClipperLike(p.clip).engine(), p.engine, "LockstakeIlkIntegration/invalid-clip-engine"); - assertEq(LockstakeClipperLike(p.clip).stopped(), 0, "LockstakeIlkIntegration/invalid-clip-stopped"); - assertEq(osmMom.osms(p.ilk), p.pip, "LockstakeIlkIntegration/invalid-osmMom-pip"); + assertEq(clip, p.clip, "checkLockstakeIlkIntegration/invalid-dog-clip"); + assertEq(engine.voteDelegateFactory(), voteDelegateFactory, "checkLockstakeIlkIntegration/invalid-engine-voteDelegateFactory"); + assertEq(engine.usdsJoin(), address(usdsJoin), "checkLockstakeIlkIntegration/invalid-engine-usdsJoin"); + assertEq(engine.ilk(), p.ilk, "checkLockstakeIlkIntegration/invalid-engine-ilk"); + assertEq(engine.mkrSky(), address(mkrSky), "checkLockstakeIlkIntegration/invalid-engine-mkrSky"); + assertEq(engine.lsmkr(), p.lsmkr, "checkLockstakeIlkIntegration/invalid-engine-lsmkr"); + assertEq(engine.jug(), address(jug), "checkLockstakeIlkIntegration/invalid-engine-jug"); + assertEq(engine.fee(), p.fee * WAD / 100_00, "checkLockstakeIlkIntegration/invalid-fee"); + assertNotEq(p.farm, address(0), "checkLockstakeIlkIntegration/invalid-farm"); + assertEq(engine.farms(p.farm), 1, "checkLockstakeIlkIntegration/disabled-farm"); + assertEq(farm.stakingToken(), p.lsmkr, "checkLockstakeIlkIntegration/invalid-stakingToken"); + assertEq(farm.rewardsToken(), p.rToken, "checkLockstakeIlkIntegration/invalid-rewardsToken"); + assertEq(farm.rewardsDistribution(), p.rDistr, "checkLockstakeIlkIntegration/invalid-rewardsDistribution"); + assertEq(farm.rewardsDuration(), p.rDur, "checkLockstakeIlkIntegration/invalid-rewardsDuration"); + assertEq(ClipAbstract(p.clip).vat(), address(vat), "checkLockstakeIlkIntegration/invalid-clip-vat"); + assertEq(ClipAbstract(p.clip).spotter(), address(spotter), "checkLockstakeIlkIntegration/invalid-clip-spotter"); + assertEq(ClipAbstract(p.clip).dog(), address(dog), "checkLockstakeIlkIntegration/invalid-clip-dog"); + assertEq(ClipAbstract(p.clip).ilk(), p.ilk, "checkLockstakeIlkIntegration/invalid-clip-ilk"); + assertEq(ClipAbstract(p.clip).vow(), address(vow), "checkLockstakeIlkIntegration/invalid-clip-vow"); + assertEq(ClipAbstract(p.clip).calc(), p.calc, "checkLockstakeIlkIntegration/invalid-clip-calc"); + assertEq(LockstakeClipperLike(p.clip).engine(), p.engine, "checkLockstakeIlkIntegration/invalid-clip-engine"); + assertEq(LockstakeClipperLike(p.clip).stopped(), 0, "checkLockstakeIlkIntegration/invalid-clip-stopped"); + assertEq(osmMom.osms(p.ilk), p.pip, "checkLockstakeIlkIntegration/invalid-osmMom-pip"); } // Check ilk registry values { @@ -1298,33 +1298,33 @@ contract DssSpellTestBase is Config, DssTest { address gemJoin, address clip ) = reg.info(p.ilk); - assertEq(name, GemAbstract(p.lsmkr).name(), "LockstakeIlkIntegration/incorrect-reg-name"); - assertEq(symbol, GemAbstract(p.lsmkr).symbol(), "LockstakeIlkIntegration/incorrect-reg-symbol"); - assertEq(_class, 7, "LockstakeIlkIntegration/incorrect-reg-class"); // REG_CLASS_JOINLESS - assertEq(decimals, GemAbstract(p.lsmkr).decimals(), "LockstakeIlkIntegration/incorrect-reg-dec"); - assertEq(gem, address(mkr), "LockstakeIlkIntegration/incorrect-reg-gem"); - assertEq(pip, p.pip, "LockstakeIlkIntegration/incorrect-reg-pip"); - assertEq(gemJoin, address(0), "LockstakeIlkIntegration/incorrect-reg-gemJoin"); - assertEq(clip, p.clip, "LockstakeIlkIntegration/incorrect-reg-xlip"); + assertEq(name, GemAbstract(p.lsmkr).name(), "checkLockstakeIlkIntegration/incorrect-reg-name"); + assertEq(symbol, GemAbstract(p.lsmkr).symbol(), "checkLockstakeIlkIntegration/incorrect-reg-symbol"); + assertEq(_class, 7, "checkLockstakeIlkIntegration/incorrect-reg-class"); // REG_CLASS_JOINLESS + assertEq(decimals, GemAbstract(p.lsmkr).decimals(), "checkLockstakeIlkIntegration/incorrect-reg-decimals"); + assertEq(gem, address(mkr), "checkLockstakeIlkIntegration/incorrect-reg-gem"); + assertEq(pip, p.pip, "checkLockstakeIlkIntegration/incorrect-reg-pip"); + assertEq(gemJoin, address(0), "checkLockstakeIlkIntegration/incorrect-reg-gemJoin"); + assertEq(clip, p.clip, "checkLockstakeIlkIntegration/incorrect-reg-clip"); } // Check required authorizations { - assertEq(vat.wards(p.engine), 1, "LockstakeIlkIntegration/missing-auth-vat-engine"); - assertEq(vat.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-vat-clip"); - assertEq(WardsAbstract(p.pip).wards(address(osmMom)), 1, "LockstakeIlkIntegration/missing-auth-pip-osmMom"); - assertEq(dog.wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-dog-clip"); - assertEq(WardsAbstract(p.lsmkr).wards(p.engine), 1, "LockstakeIlkIntegration/missing-auth-lsgem-engine"); - assertEq(WardsAbstract(p.engine).wards(p.clip), 1, "LockstakeIlkIntegration/missing-auth-engine-clip"); - assertEq(WardsAbstract(p.clip).wards(address(dog)), 1, "LockstakeIlkIntegration/missing-auth-clip-dog"); - assertEq(WardsAbstract(p.clip).wards(address(end)), 1, "LockstakeIlkIntegration/missing-auth-clip-end"); - assertEq(WardsAbstract(p.clip).wards(address(clipMom)), 1, "LockstakeIlkIntegration/missing-auth-clip-clipMom"); + assertEq(vat.wards(p.engine), 1, "checkLockstakeIlkIntegration/missing-auth-vat-engine"); + assertEq(vat.wards(p.clip), 1, "checkLockstakeIlkIntegration/missing-auth-vat-clip"); + assertEq(WardsAbstract(p.pip).wards(address(osmMom)), 1, "checkLockstakeIlkIntegration/missing-auth-pip-osmMom"); + assertEq(dog.wards(p.clip), 1, "checkLockstakeIlkIntegration/missing-auth-dog-clip"); + assertEq(WardsAbstract(p.lsmkr).wards(p.engine), 1, "checkLockstakeIlkIntegration/missing-auth-lsmkr-engine"); + assertEq(WardsAbstract(p.engine).wards(p.clip), 1, "checkLockstakeIlkIntegration/missing-auth-engine-clip"); + assertEq(WardsAbstract(p.clip).wards(address(dog)), 1, "checkLockstakeIlkIntegration/missing-auth-clip-dog"); + assertEq(WardsAbstract(p.clip).wards(address(end)), 1, "checkLockstakeIlkIntegration/missing-auth-clip-end"); + assertEq(WardsAbstract(p.clip).wards(address(clipMom)), 1, "checkLockstakeIlkIntegration/missing-auth-clip-clipMom"); } // Check required OSM buds { - assertEq(OsmAbstract(p.pip).bud(address(spotter)), 1, "LockstakeIlkIntegration/missing-spotter-bud"); - assertEq(OsmAbstract(p.pip).bud(p.clip), 1, "LockstakeIlkIntegration/missing-clip-bud"); - assertEq(OsmAbstract(p.pip).bud(address(clipMom)), 1, "LockstakeIlkIntegration/missing-clipMom-bud"); - assertEq(OsmAbstract(p.pip).bud(address(end)), 1, "LockstakeIlkIntegration/missing-end-bud"); + assertEq(OsmAbstract(p.pip).bud(address(spotter)), 1, "checkLockstakeIlkIntegration/missing-bud-spotter"); + assertEq(OsmAbstract(p.pip).bud(p.clip), 1, "checkLockstakeIlkIntegration/missing-bud-clip"); + assertEq(OsmAbstract(p.pip).bud(address(clipMom)), 1, "checkLockstakeIlkIntegration/missing-bud-clipMom"); + assertEq(OsmAbstract(p.pip).bud(address(end)), 1, "checkLockstakeIlkIntegration/missing-bud-end"); } // Prepare for liquidation uint256 drawAmt; @@ -1334,8 +1334,8 @@ contract DssSpellTestBase is Config, DssTest { vm.store(address(dog), bytes32(uint256(4)), bytes32(type(uint256).max)); // Reset auction count if (ClipAbstract(p.clip).kicks() > 0) { - vm.store(p.clip, bytes32(uint256(10)), bytes32(uint256(0))); - assertEq(ClipAbstract(p.clip).kicks(), 0, "LockstakeIlkIntegration/unchanged-kicks"); + stdstore.target(p.clip).sig("kicks()").checked_write(uint256(0)); + assertEq(ClipAbstract(p.clip).kicks(), 0, "checkLockstakeIlkIntegration/unchanged-kicks"); } // Poke OSM price OsmAbstract(p.pip).poke(); @@ -1354,45 +1354,45 @@ contract DssSpellTestBase is Config, DssTest { // Check locking and freeing Mkr { engine.open(0); - assertEq(mkr.balanceOf(address(this)), lockAmt, "LockstakeTake/LockAndFreeMkr/invalid-initial-balance"); + assertEq(mkr.balanceOf(address(this)), lockAmt, "checkLockstakeIlkIntegration/LockAndFreeMkr/invalid-initial-balance"); mkr.approve(address(engine), lockAmt); engine.lock(address(this), 0, lockAmt, 0); - assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/LockAndFreeMkr/invalid-locked-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), lockAmt, "checkLockstakeIlkIntegration/LockAndFreeMkr/invalid-locked-mkr-balance"); engine.free(address(this), 0, address(this), lockAmt); uint256 exitFee = lockAmt * p.fee / 100_00; - assertEq(mkr.balanceOf(address(this)), lockAmt - exitFee, "LockstakeTake/LockAndFreeMkr/invalid-unlocked-balance"); + assertEq(mkr.balanceOf(address(this)), lockAmt - exitFee, "checkLockstakeIlkIntegration/LockAndFreeMkr/invalid-unlocked-balance"); vm.revertTo(snapshot); } // Check locking and freeing Sky { engine.open(0); uint256 skyAmt = lockAmt * afterSpell.sky_mkr_rate; - assertEq(sky.balanceOf(address(this)), skyAmt, "LockstakeTake/LockAndFreeSky/invalid-initial-balance"); + assertEq(sky.balanceOf(address(this)), skyAmt, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-initial-balance"); sky.approve(address(engine), skyAmt); engine.lockSky(address(this), 0, skyAmt, 0); - assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/LockAndFreeSky/invalid-locked-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), lockAmt, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-locked-mkr-balance"); engine.freeSky(address(this), 0, address(this), skyAmt); uint256 exitFee = skyAmt * p.fee / 100_00; - assertEq(sky.balanceOf(address(this)), skyAmt - exitFee, "LockstakeTake/LockAndFreeSky/invalid-unlocked-balance"); + assertEq(sky.balanceOf(address(this)), skyAmt - exitFee, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-unlocked-balance"); vm.revertTo(snapshot); } // Check drawing and wiping { address urn = engine.open(0); - assertEq(mkr.balanceOf(address(this)), lockAmt, "LockstakeTake/DrawAndWipe/invalid-initial-balance"); + assertEq(mkr.balanceOf(address(this)), lockAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-initial-balance"); mkr.approve(address(engine), lockAmt); engine.lock(address(this), 0, lockAmt, 0); - assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/DrawAndWipe/invalid-locked-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), lockAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-locked-mkr-balance"); engine.draw(address(this), 0, address(this), drawAmt); - assertEq(usds.balanceOf(address(this)), drawAmt, "LockstakeTake/DrawAndWipe/invalid-usds-balance-after-draw"); + assertEq(usds.balanceOf(address(this)), drawAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-usds-balance-after-draw"); (, uint256 art) = vat.urns(p.ilk, urn); (, uint256 rate,,,) = vat.ilks(p.ilk); uint256 wipeAmt = _divup(art * rate, RAY); - assertGt(wipeAmt, drawAmt, "LockstakeTake/DrawAndWipe/invalid-wipe-after-draw"); + assertGt(wipeAmt, drawAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-wipe-after-draw"); _giveTokens(address(usds), wipeAmt); usds.approve(address(engine), wipeAmt); engine.wipe(address(this), 0, wipeAmt); - assertEq(usds.balanceOf(address(this)), 0, "LockstakeTake/DrawAndWipe/invalid-usds-balance-after-wipe"); + assertEq(usds.balanceOf(address(this)), 0, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-usds-balance-after-wipe"); vm.revertTo(snapshot); } // Check farming and getting a reward @@ -1402,7 +1402,7 @@ contract DssSpellTestBase is Config, DssTest { mkr.approve(address(engine), lockAmt); engine.selectFarm(address(this), 0, p.farm, 0); engine.lock(address(this), 0, lockAmt, 0); - assertEq(GemAbstract(p.farm).balanceOf(urn), lockAmt, "LockstakeTake/FarmAndGetReward/FarmAndGetReward/invalid-urn-farm-balance"); + assertEq(GemAbstract(p.farm).balanceOf(urn), lockAmt, "checkLockstakeIlkIntegration/FarmAndGetReward/FarmAndGetReward/invalid-urn-farm-balance"); // Deposit rewards into farm and notify uint256 rewardAmt = 1_000_000 * WAD; address rewardsToken = farm.rewardsToken(); @@ -1412,8 +1412,8 @@ contract DssSpellTestBase is Config, DssTest { address rewardsUser = address(this); skip(farm.rewardsDuration()); uint256 resultAmt = engine.getReward(address(this), 0, p.farm, rewardsUser); - assertGt(resultAmt, 0, "LockstakeTake/FarmAndGetReward/no-reward-amt"); - assertGt(GemAbstract(rewardsToken).balanceOf(rewardsUser), 0, "LockstakeTake/FarmAndGetReward/no-reward-balance"); + assertGt(resultAmt, 0, "checkLockstakeIlkIntegration/FarmAndGetReward/no-reward-amt"); + assertGt(GemAbstract(rewardsToken).balanceOf(rewardsUser), 0, "checkLockstakeIlkIntegration/FarmAndGetReward/no-reward-balance"); vm.revertTo(snapshot); } // Check liquidations @@ -1443,8 +1443,9 @@ contract DssSpellTestBase is Config, DssTest { // Open vault LockstakeEngineLike engine = LockstakeEngineLike(p.engine); vm.prank(address(123)); address voteDelegate = VoteDelegateFactoryLike(voteDelegateFactory).create(); - assertNotEq(voteDelegate, address(0), "LockstakeTake/invalid-voteDelegate-address"); + assertNotEq(voteDelegate, address(0), "checkLockstakeTake/invalid-voteDelegate-address"); address urn = engine.open(0); + uint256 initialChiefBalance = mkr.balanceOf(address(chief)); // Lock and draw if (withDelegate) { @@ -1453,76 +1454,79 @@ contract DssSpellTestBase is Config, DssTest { if (withStaking) { engine.selectFarm(address(this), 0, address(p.farm), 0); } - uint256 previousCheifBalance = mkr.balanceOf(address(chief)); mkr.approve(address(engine), lockAmt); engine.lock(address(this), 0, lockAmt, 0); engine.draw(address(this), 0, address(this), drawAmt); if (withDelegate) { - assertEq(engine.urnVoteDelegates(urn), voteDelegate, "LockstakeTake/AfterLockDraw/withDelegate/invalid-voteDelegate-urn"); - assertEq(mkr.balanceOf(address(chief)) - previousCheifBalance, lockAmt, "LockstakeTake/AfterLockDraw/withDelegate/invalid-chief-mkr-balance"); - assertEq(mkr.balanceOf(p.engine), 0, "LockstakeTake/AfterLockDraw/withDelegate/invalid-engine-balance"); + assertEq(engine.urnVoteDelegates(urn), voteDelegate, "checkLockstakeTake/AfterLockDraw/withDelegate/invalid-voteDelegate-urn"); + assertEq(mkr.balanceOf(address(chief)) - initialChiefBalance, lockAmt, "checkLockstakeTake/AfterLockDraw/withDelegate/invalid-chief-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), 0, "checkLockstakeTake/AfterLockDraw/withDelegate/invalid-engine-balance"); } else { - assertEq(engine.urnVoteDelegates(urn), address(0), "LockstakeTake/AfterLockDraw/withoutDelegate/invalid-voteDelegate-urn"); - assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/AfterLockDraw/withoutDelegate/invalid-engine-balance"); + assertEq(engine.urnVoteDelegates(urn), address(0), "checkLockstakeTake/AfterLockDraw/withoutDelegate/invalid-voteDelegate-urn"); + assertEq(mkr.balanceOf(address(chief)), initialChiefBalance, "checkLockstakeTake/AfterLockDraw/withoutDelegate/invalid-chief-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), lockAmt, "checkLockstakeTake/AfterLockDraw/withoutDelegate/invalid-engine-balance"); } if (withStaking) { - assertEq(GemAbstract(p.lsmkr).balanceOf(urn), 0, "LockstakeTake/AfterLockDraw/withStaking/invalid-urn-lsgem-balance"); - assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), lockAmt, "LockstakeTake/AfterLockDraw/withStaking/invalid-farm-lsgem-balance"); - assertEq(GemAbstract(p.farm).balanceOf(urn), lockAmt, "LockstakeTake/AfterLockDraw/withStaking/invalid-urn-farm-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(urn), 0, "checkLockstakeTake/AfterLockDraw/withStaking/invalid-urn-lsgem-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), lockAmt, "checkLockstakeTake/AfterLockDraw/withStaking/invalid-farm-lsgem-balance"); + assertEq(GemAbstract(p.farm).balanceOf(urn), lockAmt, "checkLockstakeTake/AfterLockDraw/withStaking/invalid-urn-farm-balance"); } else { - assertEq(GemAbstract(p.lsmkr).balanceOf(urn), lockAmt, "LockstakeTake/AfterLockDraw/withoutStaking/invalid-urn-lsgem-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(urn), lockAmt, "checkLockstakeTake/AfterLockDraw/withoutStaking/invalid-urn-lsgem-balance"); } // Force liquidation if (withDelegate) { - vm.roll(block.number + 1); // Roll one block to allow freeing from cheif + vm.roll(block.number + 1); // Roll one block to allow freeing from chief } _setIlkMat(p.ilk, 100_000 * RAY); spotter.poke(p.ilk); - assertEq(ClipAbstract(p.clip).kicks(), 0, "LockstakeTake/non-0-kicks"); - assertEq(engine.urnAuctions(urn), 0, "LockstakeTake/non-0-actions"); + assertEq(ClipAbstract(p.clip).kicks(), 0, "checkLockstakeTake/non-0-kicks"); + assertEq(engine.urnAuctions(urn), 0, "checkLockstakeTake/non-0-actions"); uint256 id = dog.bark(p.ilk, urn, address(this)); - assertEq(ClipAbstract(p.clip).kicks(), 1, "LockstakeTake/AfterBark/no-kicks"); - assertEq(engine.urnAuctions(urn), 1, "LockstakeTake/AfterBark/no-actions"); + assertEq(ClipAbstract(p.clip).kicks(), 1, "checkLockstakeTake/AfterBark/no-kicks"); + assertEq(engine.urnAuctions(urn), 1, "checkLockstakeTake/AfterBark/no-actions"); Sale memory sale; (sale.pos, sale.tab, sale.lot, sale.tot, sale.usr, sale.tic, sale.top) = LockstakeClipperLike(p.clip).sales(id); - assertEq(sale.pos, 0, "LockstakeTake/AfterBark/invalid-sale.pos"); - assertGt(sale.tab, drawAmt * RAY, "LockstakeTake/AfterBark/invalid-sale.tab"); - assertEq(sale.lot, lockAmt, "LockstakeTake/AfterBark/invalid-sale.lot"); - assertEq(sale.tot, lockAmt, "LockstakeTake/AfterBark/invalid-sale.tot"); - assertEq(sale.usr, urn, "LockstakeTake/AfterBark/invalid-sale.usr"); - assertEq(sale.tic, block.timestamp, "LockstakeTake/AfterBark/invalid-sale.tic"); - assertEq(sale.top, _getOSMPrice(p.pip) * ClipAbstract(p.clip).buf() / WAD, "LockstakeTake/AfterBark/invalid-sale.top"); - assertEq(vat.gem(p.ilk, p.clip), lockAmt, "LockstakeTake/AfterBark/invalid-vat-gem-clip"); - assertEq(mkr.balanceOf(p.engine), lockAmt, "LockstakeTake/AfterBark/invalid-engine-mkr-balance"); - assertEq(GemAbstract(p.lsmkr).balanceOf(urn), 0, "LockstakeTake/AfterBark/invalid-urn-lsgem-balance"); + assertEq(sale.pos, 0, "checkLockstakeTake/AfterBark/invalid-sale.pos"); + assertGt(sale.tab, drawAmt * RAY, "checkLockstakeTake/AfterBark/invalid-sale.tab"); + assertEq(sale.lot, lockAmt, "checkLockstakeTake/AfterBark/invalid-sale.lot"); + assertEq(sale.tot, lockAmt, "checkLockstakeTake/AfterBark/invalid-sale.tot"); + assertEq(sale.usr, urn, "checkLockstakeTake/AfterBark/invalid-sale.usr"); + assertEq(sale.tic, block.timestamp, "checkLockstakeTake/AfterBark/invalid-sale.tic"); + assertEq(sale.top, _getOSMPrice(p.pip) * ClipAbstract(p.clip).buf() / WAD, "checkLockstakeTake/AfterBark/invalid-sale.top"); + assertEq(vat.gem(p.ilk, p.clip), lockAmt, "checkLockstakeTake/AfterBark/invalid-vat-gem-clip"); + assertEq(mkr.balanceOf(p.engine), lockAmt, "checkLockstakeTake/AfterBark/invalid-engine-mkr-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(urn), 0, "checkLockstakeTake/AfterBark/invalid-urn-lsgem-balance"); if (withDelegate) { - assertEq(mkr.balanceOf(voteDelegate), 0, "LockstakeTake/AfterBark/withDelegate/invalid-voteDelegate-mkr-balance"); + assertEq(mkr.balanceOf(address(chief)), initialChiefBalance, "checkLockstakeTake/AfterBark/withDelegate/invalid-chief-mkr-balance"); } if (withStaking) { - assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), 0, "LockstakeTake/AfterBark/withStaking/invalid-farm-lsgem-balance"); - assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "LockstakeTake/AfterBark/withStaking/invalid-urn-farm-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), 0, "checkLockstakeTake/AfterBark/withStaking/invalid-farm-lsgem-balance"); + assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "checkLockstakeTake/AfterBark/withStaking/invalid-urn-farm-balance"); } // Take auction address buyer = address(888); vm.prank(pauseProxy); vat.suck(address(0), buyer, sale.tab); vm.prank(buyer); vat.hope(p.clip); - assertEq(mkr.balanceOf(buyer), 0, "LockstakeTake/AfterBark/invalid-buyer-mkr-balance"); + assertEq(mkr.balanceOf(buyer), 0, "checkLockstakeTake/AfterBark/invalid-buyer-mkr-balance"); vm.prank(buyer); ClipAbstract(p.clip).take(id, lockAmt, type(uint256).max, buyer, ""); - assertGt(mkr.balanceOf(buyer), 0, "LockstakeTake/AfterTake/invalid-buyer-mkr-balance"); + assertGt(mkr.balanceOf(buyer), 0, "checkLockstakeTake/AfterTake/invalid-buyer-mkr-balance"); (sale.pos, sale.tab, sale.lot, sale.tot, sale.usr, sale.tic, sale.top) = LockstakeClipperLike(p.clip).sales(id); - assertEq(sale.pos, 0, "LockstakeTake/AfterTake/invalid-sale.pos"); - assertEq(sale.tab, 0, "LockstakeTake/AfterTake/invalid-sale.tab"); - assertEq(sale.lot, 0, "LockstakeTake/AfterTake/invalid-sale.lot"); - assertEq(sale.tot, 0, "LockstakeTake/AfterTake/invalid-sale.tot"); - assertEq(sale.usr, address(0), "LockstakeTake/AfterTake/invalid-sale.usr"); - assertEq(sale.tic, 0, "LockstakeTake/AfterTake/invalid-sale.tic"); - assertEq(sale.top, 0, "LockstakeTake/AfterTake/invalid-sale.top"); - assertEq(vat.gem(p.ilk, p.clip), 0, "LockstakeTake/AfterTake/invalid-vat.gem"); + assertEq(sale.pos, 0, "checkLockstakeTake/AfterTake/invalid-sale.pos"); + assertEq(sale.tab, 0, "checkLockstakeTake/AfterTake/invalid-sale.tab"); + assertEq(sale.lot, 0, "checkLockstakeTake/AfterTake/invalid-sale.lot"); + assertEq(sale.tot, 0, "checkLockstakeTake/AfterTake/invalid-sale.tot"); + assertEq(sale.usr, address(0), "checkLockstakeTake/AfterTake/invalid-sale.usr"); + assertEq(sale.tic, 0, "checkLockstakeTake/AfterTake/invalid-sale.tic"); + assertEq(sale.top, 0, "checkLockstakeTake/AfterTake/invalid-sale.top"); + assertEq(vat.gem(p.ilk, p.clip), 0, "checkLockstakeTake/AfterTake/invalid-vat.gem"); + if (withDelegate) { + assertEq(mkr.balanceOf(address(chief)), initialChiefBalance, "checkLockstakeTake/AfterTake/withDelegate/invalid-chief-mkr-balance"); + } if (withStaking) { - assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), 0, "LockstakeTake/AfterTake/withStaking/invalid-lsgem-farm-balance"); - assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "LockstakeTake/AfterTake/withStaking/invalid-farm-urn-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), 0, "checkLockstakeTake/AfterTake/withStaking/invalid-lsgem-farm-balance"); + assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "checkLockstakeTake/AfterTake/withStaking/invalid-farm-urn-balance"); } } From f904bd63b1595e5b2268f3af022c794cf2eaa765 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Thu, 17 Oct 2024 11:08:26 +0200 Subject: [PATCH 36/44] make lockstake test more robust --- src/DssSpell.t.base.sol | 49 +++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index a438068d0..9c3b17ee4 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -1353,11 +1353,12 @@ contract DssSpellTestBase is Config, DssTest { uint256 snapshot = vm.snapshot(); // Check locking and freeing Mkr { + uint256 initialEngineBalance = mkr.balanceOf(p.engine); engine.open(0); assertEq(mkr.balanceOf(address(this)), lockAmt, "checkLockstakeIlkIntegration/LockAndFreeMkr/invalid-initial-balance"); mkr.approve(address(engine), lockAmt); engine.lock(address(this), 0, lockAmt, 0); - assertEq(mkr.balanceOf(p.engine), lockAmt, "checkLockstakeIlkIntegration/LockAndFreeMkr/invalid-locked-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), initialEngineBalance + lockAmt, "checkLockstakeIlkIntegration/LockAndFreeMkr/invalid-locked-mkr-balance"); engine.free(address(this), 0, address(this), lockAmt); uint256 exitFee = lockAmt * p.fee / 100_00; assertEq(mkr.balanceOf(address(this)), lockAmt - exitFee, "checkLockstakeIlkIntegration/LockAndFreeMkr/invalid-unlocked-balance"); @@ -1365,12 +1366,13 @@ contract DssSpellTestBase is Config, DssTest { } // Check locking and freeing Sky { + uint256 initialEngineBalance = mkr.balanceOf(p.engine); engine.open(0); uint256 skyAmt = lockAmt * afterSpell.sky_mkr_rate; assertEq(sky.balanceOf(address(this)), skyAmt, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-initial-balance"); sky.approve(address(engine), skyAmt); engine.lockSky(address(this), 0, skyAmt, 0); - assertEq(mkr.balanceOf(p.engine), lockAmt, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-locked-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), initialEngineBalance + lockAmt, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-locked-mkr-balance"); engine.freeSky(address(this), 0, address(this), skyAmt); uint256 exitFee = skyAmt * p.fee / 100_00; assertEq(sky.balanceOf(address(this)), skyAmt - exitFee, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-unlocked-balance"); @@ -1378,11 +1380,12 @@ contract DssSpellTestBase is Config, DssTest { } // Check drawing and wiping { + uint256 initialEngineBalance = mkr.balanceOf(p.engine); address urn = engine.open(0); assertEq(mkr.balanceOf(address(this)), lockAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-initial-balance"); mkr.approve(address(engine), lockAmt); engine.lock(address(this), 0, lockAmt, 0); - assertEq(mkr.balanceOf(p.engine), lockAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-locked-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), initialEngineBalance + lockAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-locked-mkr-balance"); engine.draw(address(this), 0, address(this), drawAmt); assertEq(usds.balanceOf(address(this)), drawAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-usds-balance-after-draw"); (, uint256 art) = vat.urns(p.ilk, urn); @@ -1433,6 +1436,13 @@ contract DssSpellTestBase is Config, DssTest { uint256 top; // Starting price [ray] } + struct LockstakeBalances { + uint256 chiefMkr; + uint256 engineMkr; + uint256 farmLsmkr; + uint256 vatGem; + } + function _checkLockstakeTake( LockstakeIlkParams memory p, uint256 lockAmt, @@ -1445,7 +1455,12 @@ contract DssSpellTestBase is Config, DssTest { vm.prank(address(123)); address voteDelegate = VoteDelegateFactoryLike(voteDelegateFactory).create(); assertNotEq(voteDelegate, address(0), "checkLockstakeTake/invalid-voteDelegate-address"); address urn = engine.open(0); - uint256 initialChiefBalance = mkr.balanceOf(address(chief)); + LockstakeBalances memory initialBalances = LockstakeBalances({ + chiefMkr: mkr.balanceOf(address(chief)), + engineMkr: mkr.balanceOf(p.engine), + farmLsmkr: GemAbstract(p.lsmkr).balanceOf(p.farm), + vatGem: vat.gem(p.ilk, p.clip) + }); // Lock and draw if (withDelegate) { @@ -1459,19 +1474,21 @@ contract DssSpellTestBase is Config, DssTest { engine.draw(address(this), 0, address(this), drawAmt); if (withDelegate) { assertEq(engine.urnVoteDelegates(urn), voteDelegate, "checkLockstakeTake/AfterLockDraw/withDelegate/invalid-voteDelegate-urn"); - assertEq(mkr.balanceOf(address(chief)) - initialChiefBalance, lockAmt, "checkLockstakeTake/AfterLockDraw/withDelegate/invalid-chief-mkr-balance"); - assertEq(mkr.balanceOf(p.engine), 0, "checkLockstakeTake/AfterLockDraw/withDelegate/invalid-engine-balance"); + assertEq(mkr.balanceOf(address(chief)) - initialBalances.chiefMkr, lockAmt, "checkLockstakeTake/AfterLockDraw/withDelegate/invalid-chief-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), initialBalances.engineMkr, "checkLockstakeTake/AfterLockDraw/withDelegate/invalid-engine-balance"); } else { assertEq(engine.urnVoteDelegates(urn), address(0), "checkLockstakeTake/AfterLockDraw/withoutDelegate/invalid-voteDelegate-urn"); - assertEq(mkr.balanceOf(address(chief)), initialChiefBalance, "checkLockstakeTake/AfterLockDraw/withoutDelegate/invalid-chief-mkr-balance"); - assertEq(mkr.balanceOf(p.engine), lockAmt, "checkLockstakeTake/AfterLockDraw/withoutDelegate/invalid-engine-balance"); + assertEq(mkr.balanceOf(address(chief)), initialBalances.chiefMkr, "checkLockstakeTake/AfterLockDraw/withoutDelegate/invalid-chief-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), initialBalances.engineMkr + lockAmt, "checkLockstakeTake/AfterLockDraw/withoutDelegate/invalid-engine-balance"); } if (withStaking) { assertEq(GemAbstract(p.lsmkr).balanceOf(urn), 0, "checkLockstakeTake/AfterLockDraw/withStaking/invalid-urn-lsgem-balance"); - assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), lockAmt, "checkLockstakeTake/AfterLockDraw/withStaking/invalid-farm-lsgem-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), initialBalances.farmLsmkr + lockAmt, "checkLockstakeTake/AfterLockDraw/withStaking/invalid-farm-lsgem-balance"); assertEq(GemAbstract(p.farm).balanceOf(urn), lockAmt, "checkLockstakeTake/AfterLockDraw/withStaking/invalid-urn-farm-balance"); } else { assertEq(GemAbstract(p.lsmkr).balanceOf(urn), lockAmt, "checkLockstakeTake/AfterLockDraw/withoutStaking/invalid-urn-lsgem-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), initialBalances.farmLsmkr, "checkLockstakeTake/AfterLockDraw/withoutStaking/invalid-farm-lsgem-balance"); + assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "checkLockstakeTake/AfterLockDraw/withoutStaking/invalid-urn-farm-balance"); } // Force liquidation @@ -1494,14 +1511,14 @@ contract DssSpellTestBase is Config, DssTest { assertEq(sale.usr, urn, "checkLockstakeTake/AfterBark/invalid-sale.usr"); assertEq(sale.tic, block.timestamp, "checkLockstakeTake/AfterBark/invalid-sale.tic"); assertEq(sale.top, _getOSMPrice(p.pip) * ClipAbstract(p.clip).buf() / WAD, "checkLockstakeTake/AfterBark/invalid-sale.top"); - assertEq(vat.gem(p.ilk, p.clip), lockAmt, "checkLockstakeTake/AfterBark/invalid-vat-gem-clip"); - assertEq(mkr.balanceOf(p.engine), lockAmt, "checkLockstakeTake/AfterBark/invalid-engine-mkr-balance"); + assertEq(vat.gem(p.ilk, p.clip), initialBalances.vatGem + lockAmt, "checkLockstakeTake/AfterBark/invalid-vat-gem-clip"); + assertEq(mkr.balanceOf(p.engine), initialBalances.engineMkr + lockAmt, "checkLockstakeTake/AfterBark/invalid-engine-mkr-balance"); assertEq(GemAbstract(p.lsmkr).balanceOf(urn), 0, "checkLockstakeTake/AfterBark/invalid-urn-lsgem-balance"); if (withDelegate) { - assertEq(mkr.balanceOf(address(chief)), initialChiefBalance, "checkLockstakeTake/AfterBark/withDelegate/invalid-chief-mkr-balance"); + assertEq(mkr.balanceOf(address(chief)), initialBalances.chiefMkr, "checkLockstakeTake/AfterBark/withDelegate/invalid-chief-mkr-balance"); } if (withStaking) { - assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), 0, "checkLockstakeTake/AfterBark/withStaking/invalid-farm-lsgem-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), initialBalances.farmLsmkr, "checkLockstakeTake/AfterBark/withStaking/invalid-farm-lsgem-balance"); assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "checkLockstakeTake/AfterBark/withStaking/invalid-urn-farm-balance"); } @@ -1520,12 +1537,12 @@ contract DssSpellTestBase is Config, DssTest { assertEq(sale.usr, address(0), "checkLockstakeTake/AfterTake/invalid-sale.usr"); assertEq(sale.tic, 0, "checkLockstakeTake/AfterTake/invalid-sale.tic"); assertEq(sale.top, 0, "checkLockstakeTake/AfterTake/invalid-sale.top"); - assertEq(vat.gem(p.ilk, p.clip), 0, "checkLockstakeTake/AfterTake/invalid-vat.gem"); + assertEq(vat.gem(p.ilk, p.clip), initialBalances.vatGem, "checkLockstakeTake/AfterTake/invalid-vat.gem"); if (withDelegate) { - assertEq(mkr.balanceOf(address(chief)), initialChiefBalance, "checkLockstakeTake/AfterTake/withDelegate/invalid-chief-mkr-balance"); + assertEq(mkr.balanceOf(address(chief)), initialBalances.chiefMkr, "checkLockstakeTake/AfterTake/withDelegate/invalid-chief-mkr-balance"); } if (withStaking) { - assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), 0, "checkLockstakeTake/AfterTake/withStaking/invalid-lsgem-farm-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), initialBalances.farmLsmkr, "checkLockstakeTake/AfterTake/withStaking/invalid-lsgem-farm-balance"); assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "checkLockstakeTake/AfterTake/withStaking/invalid-farm-urn-balance"); } } From 40d5caa590c25349244daf7afd8ee0f8f0ae89da Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Thu, 17 Oct 2024 11:22:43 +0200 Subject: [PATCH 37/44] drip before wiping --- src/DssSpell.t.base.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index 9c3b17ee4..bddbf099d 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -1388,10 +1388,12 @@ contract DssSpellTestBase is Config, DssTest { assertEq(mkr.balanceOf(p.engine), initialEngineBalance + lockAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-locked-mkr-balance"); engine.draw(address(this), 0, address(this), drawAmt); assertEq(usds.balanceOf(address(this)), drawAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-usds-balance-after-draw"); + skip(10 days); + jug.drip(p.ilk); (, uint256 art) = vat.urns(p.ilk, urn); (, uint256 rate,,,) = vat.ilks(p.ilk); uint256 wipeAmt = _divup(art * rate, RAY); - assertGt(wipeAmt, drawAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-wipe-after-draw"); + assertGt(wipeAmt, drawAmt + 1 /* +1 to exclude rounding up */, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-wipe-after-draw"); _giveTokens(address(usds), wipeAmt); usds.approve(address(engine), wipeAmt); engine.wipe(address(this), 0, wipeAmt); From 224f53fee417e3b17a915bf8de3272c5e3becfc7 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Thu, 17 Oct 2024 12:39:00 +0200 Subject: [PATCH 38/44] enable testMedianReaders --- src/DssSpell.t.sol | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index 4b41e6b7d..d6283f4f0 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -349,7 +349,7 @@ contract DssSpellTest is DssSpellTestBase { } } - function testOSMs() public { // add the `skipped` modifier to skip + function testOsmReaders() public { // add the `skipped` modifier to skip address OSM = addr.addr("PIP_MKR"); address[4] memory newReaders = [ addr.addr("MCD_SPOT"), @@ -371,6 +371,25 @@ contract DssSpellTest is DssSpellTestBase { } } + function testMedianReaders() public { // add the `skipped` modifier to skip + address median = chainLog.getAddress("PIP_MKR"); // PIP_MKR before spell + address[1] memory newReaders = [ + addr.addr('PIP_MKR') // PIP_MKR after spell + ]; + + for (uint256 i = 0; i < newReaders.length; i++) { + assertEq(MedianAbstract(median).bud(newReaders[i]), 0); + } + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + for (uint256 i = 0; i < newReaders.length; i++) { + assertEq(MedianAbstract(median).bud(newReaders[i]), 1); + } + } + struct Authorization { bytes32 base; bytes32 ward; @@ -406,17 +425,6 @@ contract DssSpellTest is DssSpellTestBase { } } - function testMedianizers() public skipped { // add the `skipped` modifier to skip - _vote(address(spell)); - _scheduleWaitAndCast(address(spell)); - assertTrue(spell.done(), "TestError/spell-not-done"); - - // Track Median authorizations here - address SET_TOKEN = address(0); - address TOKENUSD_MED = OsmAbstract(addr.addr("PIP_TOKEN")).src(); - assertEq(MedianAbstract(TOKENUSD_MED).bud(SET_TOKEN), 1); - } - function testVestDAI() public skipped { // add the `skipped` modifier to skip // Provide human-readable names for timestamps uint256 DEC_01_2023 = 1701385200; From b8443d66a4f522167fd843995674336a552eca37 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Thu, 17 Oct 2024 13:16:37 +0200 Subject: [PATCH 39/44] add testNewOsmMomAddition --- src/DssSpell.t.sol | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/DssSpell.t.sol b/src/DssSpell.t.sol index d6283f4f0..5c668e7e8 100644 --- a/src/DssSpell.t.sol +++ b/src/DssSpell.t.sol @@ -1001,4 +1001,21 @@ contract DssSpellTest is DssSpellTestBase { assertEq(spot, 0, _concat("TestError/non-zero-spot-price-after-cull-", ilk)); } } + + function testNewOsmMomAddition() public { + bytes32 ilk = "LSE-MKR-A"; + address osm = addr.addr("PIP_MKR"); + + assertEq(osmMom.osms(ilk), address(0), "TestError/osm-already-in-mom"); + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + assertEq(osmMom.osms(ilk), osm, "TestError/osm-not-in-mom"); + + assertEq(OsmAbstract(osm).stopped(), 0, "TestError/unexpected-stopped-before"); + vm.prank(chief.hat()); osmMom.stop(ilk); + assertEq(OsmAbstract(osm).stopped(), 1, "TestError/unexpected-stopped-after"); + } } From f534d3f7638b584c64cebb1478656c848795c24c Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Thu, 17 Oct 2024 13:48:37 +0200 Subject: [PATCH 40/44] fix fragile freeSky test --- src/DssSpell.t.base.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index bddbf099d..b68430b99 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -1374,8 +1374,8 @@ contract DssSpellTestBase is Config, DssTest { engine.lockSky(address(this), 0, skyAmt, 0); assertEq(mkr.balanceOf(p.engine), initialEngineBalance + lockAmt, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-locked-mkr-balance"); engine.freeSky(address(this), 0, address(this), skyAmt); - uint256 exitFee = skyAmt * p.fee / 100_00; - assertEq(sky.balanceOf(address(this)), skyAmt - exitFee, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-unlocked-balance"); + uint256 exitFee = lockAmt * p.fee / 100_00 * afterSpell.sky_mkr_rate; + assertGe(sky.balanceOf(address(this)), skyAmt - exitFee, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-unlocked-balance"); vm.revertTo(snapshot); } // Check drawing and wiping From 6d74b2d9ec2950f2fb04088013b39210e1513e21 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Thu, 17 Oct 2024 16:39:48 +0200 Subject: [PATCH 41/44] update exec doc url and hash --- src/DssSpell.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index a5d988232..39bb4aa94 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -46,9 +46,9 @@ interface ProxyLike { 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/20b4c0ed4bb2771483a0861747cf34a25080ad21/governance/votes/Executive%20vote%20-%20October%2017%2C%202024.md' -q -O - 2>/dev/null)" + // Hash: cast keccak -- "$(wget 'https://raw.githubusercontent.com/makerdao/community/7c7d7d16734407fdde827801ab4bbd6878560375/governance/votes/Executive%20vote%20-%20October%2017%2C%202024.md' -q -O - 2>/dev/null)" string public constant override description = - "2024-10-17 MakerDAO Executive Spell | Hash: 0xcbbb4fa0c3bce6e8d97c46e0d4a7aba50d42b184a6f58c0f1b1cf2e0da849858"; + "2024-10-17 MakerDAO Executive Spell | Hash: 0xa1e0345f807a0333170271e69caca6d384b3a715ccad597b8ad9502963eabd6f"; // Set office hours according to the summary function officeHours() public pure override returns (bool) { From 95c68b5bad2faa03900bd8c973436bf6584fc880 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Thu, 17 Oct 2024 16:50:05 +0200 Subject: [PATCH 42/44] update instructions in the spell --- src/DssSpell.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/DssSpell.sol b/src/DssSpell.sol index 39bb4aa94..ffb647de1 100644 --- a/src/DssSpell.sol +++ b/src/DssSpell.sol @@ -320,7 +320,7 @@ contract DssSpellAction is DssAction { // New collateral type "LSE-MKR-A" will be added to LINE_MOM // New collateral type "LSE-MKR-A" will be added to auto-line using provided maxLine, gap and ttl // New collateral type "LSE-MKR-A" will be added to ILK_REGISTRY with provided values ("name", "symbol") and the new ilk class 7 - // New MKR OSM will allow MCD_SPOT, CLIPPER_MOM, OSM_MOM, MCD_END and LockstakeClipper to access its price + // The new MKR OSM will allow MCD_SPOT, CLIPPER_MOM, LOCKSTAKE_CLIP, MCD_END to access its price. // PIP_MKR will be added to OSM_MOM // LockstakeClipper will be configured using provided values ("buf", "tail", "cusp", "chip", "tip", "stopped", "clip", "tolerance") // StairstepExponentialDecrease calc contract will be configured using provided values ("cut", "step") @@ -329,6 +329,7 @@ contract DssSpellAction is DssAction { // LockstakeClipper will be authorized to access "vat" and LockstakeEngine // CLIPPER_MOM, MCD_DOG and MCD_END will be authorized to access LockstakeClipper // New chainlog keys LOCKSTAKE_MKR, LOCKSTAKE_ENGINE, LOCKSTAKE_CLIP and LOCKSTAKE_CLIP_CALC will be added + // OSM_MOM will be authorized to access the new MKR OSM // Note: above instructions are taken inside LockstakeInit.initLockstake method // ---------- Fund Early Bird Rewards Multisig ---------- From e61feb43f02c62c7a9efdc1c77f64fb4e61ae5c7 Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Thu, 17 Oct 2024 22:55:28 +0200 Subject: [PATCH 43/44] add deployed spell info --- src/test/config.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/config.sol b/src/test/config.sol index 61257e581..d43259025 100644 --- a/src/test/config.sol +++ b/src/test/config.sol @@ -104,9 +104,9 @@ contract Config { // Values for spell-specific parameters // spellValues = SpellValues({ - 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: 0, // use `make deploy-info tx=` to obtain the block number + deployed_spell: address(0xACA9a90C92647e3d3f04095118192DC80C470955), // populate with deployed spell if deployed + deployed_spell_created: 1729197815, // use `make deploy-info tx=` to obtain the timestamp + deployed_spell_block: 20987749, // use `make deploy-info tx=` to obtain the block number previous_spells: prevSpells, // older spells to ensure are executed first office_hours_enabled: true, // true if officehours is expected to be enabled in the spell expiration_threshold: 30 days // Amount of time before spell expires From 9cc33691f077fe6136d8a0f84b0528be4bd9d0cf Mon Sep 17 00:00:00 2001 From: SidestreamColdMelon Date: Thu, 17 Oct 2024 23:04:07 +0200 Subject: [PATCH 44/44] archived spell --- archive/2024-10-17-DssSpell/DssSpell.sol | 411 +++ .../2024-10-17-DssSpell/DssSpell.t.base.sol | 3178 +++++++++++++++++ archive/2024-10-17-DssSpell/DssSpell.t.sol | 1021 ++++++ .../dependencies/dss-flappers/FlapperInit.sol | 211 ++ .../dss-flappers/SplitterInstance.sol | 22 + .../dependencies/lockstake/LockstakeInit.sol | 259 ++ .../lockstake/LockstakeInstance.sol | 24 + .../test/addresses_deployers.sol | 63 + .../test/addresses_mainnet.sol | 522 +++ .../test/addresses_wallets.sol | 200 ++ archive/2024-10-17-DssSpell/test/config.sol | 1853 ++++++++++ archive/2024-10-17-DssSpell/test/rates.sol | 472 +++ .../2024-10-17-DssSpell/test/starknet.t.sol | 242 ++ 13 files changed, 8478 insertions(+) create mode 100644 archive/2024-10-17-DssSpell/DssSpell.sol create mode 100644 archive/2024-10-17-DssSpell/DssSpell.t.base.sol create mode 100644 archive/2024-10-17-DssSpell/DssSpell.t.sol create mode 100644 archive/2024-10-17-DssSpell/dependencies/dss-flappers/FlapperInit.sol create mode 100644 archive/2024-10-17-DssSpell/dependencies/dss-flappers/SplitterInstance.sol create mode 100644 archive/2024-10-17-DssSpell/dependencies/lockstake/LockstakeInit.sol create mode 100644 archive/2024-10-17-DssSpell/dependencies/lockstake/LockstakeInstance.sol create mode 100644 archive/2024-10-17-DssSpell/test/addresses_deployers.sol create mode 100644 archive/2024-10-17-DssSpell/test/addresses_mainnet.sol create mode 100644 archive/2024-10-17-DssSpell/test/addresses_wallets.sol create mode 100644 archive/2024-10-17-DssSpell/test/config.sol create mode 100644 archive/2024-10-17-DssSpell/test/rates.sol create mode 100644 archive/2024-10-17-DssSpell/test/starknet.t.sol diff --git a/archive/2024-10-17-DssSpell/DssSpell.sol b/archive/2024-10-17-DssSpell/DssSpell.sol new file mode 100644 index 000000000..ffb647de1 --- /dev/null +++ b/archive/2024-10-17-DssSpell/DssSpell.sol @@ -0,0 +1,411 @@ +// SPDX-FileCopyrightText: © 2020 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity 0.8.16; + +import "dss-exec-lib/DssExec.sol"; +import "dss-exec-lib/DssAction.sol"; + +import { DssInstance, MCD } from "dss-test/MCD.sol"; +import { VatAbstract } from "dss-interfaces/dss/VatAbstract.sol"; + +// Note: source code matches https://github.com/makerdao/dss-flappers/blob/95431f3d4da66babf81c6e1138bd05f5ddc5e516/deploy/FlapperInit.sol +import { FlapperInit, FarmConfig } from "src/dependencies/dss-flappers/FlapperInit.sol"; + +// Note: source code matches https://github.com/makerdao/lockstake/blob/7c71318623f5d6732457fd0c247a1f1760960011/deploy/LockstakeInit.sol +import { LockstakeInit, LockstakeConfig } from "src/dependencies/lockstake/LockstakeInit.sol"; +// Note: source code matches https://github.com/makerdao/lockstake/blob/7c71318623f5d6732457fd0c247a1f1760960011/deploy/LockstakeInstance.sol +import { LockstakeInstance } from "src/dependencies/lockstake/LockstakeInstance.sol"; + +interface SkyLike { + function mint(address to, uint256 value) external; +} + +interface RwaLiquidationOracleLike { + function cull(bytes32 ilk, address urn) external; + function tell(bytes32 ilk) external; +} + +interface ProxyLike { + function exec(address target, bytes calldata args) external payable returns (bytes memory out); +} + +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/7c7d7d16734407fdde827801ab4bbd6878560375/governance/votes/Executive%20vote%20-%20October%2017%2C%202024.md' -q -O - 2>/dev/null)" + string public constant override description = + "2024-10-17 MakerDAO Executive Spell | Hash: 0xa1e0345f807a0333170271e69caca6d384b3a715ccad597b8ad9502963eabd6f"; + + // Set office hours according to the summary + function officeHours() public pure override returns (bool) { + return true; + } + + // Note: by the previous convention it should be a comma-separated list of DAO resolutions IPFS hashes + string public constant dao_resolutions = "QmYJUvw5xbAJmJknG2xUKDLe424JSTWQQhbJCnucRRjUv7"; + + // ---------- Math ---------- + uint256 internal constant MILLION = 10 ** 6; + uint256 internal constant WAD = 10 ** 18; + uint256 internal constant RAY = 10 ** 27; + uint256 internal constant RAD = 10 ** 45; + + // ---------- 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 = ; + uint256 internal constant TWELVE_PCT_RATE = 1000000003593629043335673582; + + // ---------- Contracts ---------- + address internal immutable MCD_VAT = DssExecLib.vat(); + address internal immutable MCD_VOW = DssExecLib.vow(); + address internal immutable MCD_GOV = DssExecLib.mkr(); + address internal immutable MIP21_LIQUIDATION_ORACLE = DssExecLib.getChangelogAddress("MIP21_LIQUIDATION_ORACLE"); + address internal immutable RWA007_A_URN = DssExecLib.getChangelogAddress("RWA007_A_URN"); + address internal immutable RWA014_A_URN = DssExecLib.getChangelogAddress("RWA014_A_URN"); + address internal immutable PIP_MKR = DssExecLib.getChangelogAddress("PIP_MKR"); + address internal immutable VOTE_DELEGATE_PROXY_FACTORY = DssExecLib.getChangelogAddress("VOTE_DELEGATE_PROXY_FACTORY"); + address internal immutable MCD_SPLIT = DssExecLib.getChangelogAddress("MCD_SPLIT"); + address internal immutable USDS_JOIN = DssExecLib.getChangelogAddress("USDS_JOIN"); + address internal immutable USDS = DssExecLib.getChangelogAddress("USDS"); + address internal immutable MKR_SKY = DssExecLib.getChangelogAddress("MKR_SKY"); + address internal immutable SKY = DssExecLib.getChangelogAddress("SKY"); + address internal constant NEW_PIP_MKR = 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b; + address internal constant VOTE_DELEGATE_FACTORY = 0xC3D809E87A2C9da4F6d98fECea9135d834d6F5A0; + address internal constant REWARDS_LSMKR_USDS = 0x92282235a39bE957fF1f37619fD22A9aE5507CB1; + address internal constant LOCKSTAKE_MKR = 0xb4e0e45e142101dC3Ed768bac219fC35EDBED295; + address internal constant LOCKSTAKE_ENGINE = 0x2b16C07D5fD5cC701a0a871eae2aad6DA5fc8f12; + address internal constant LOCKSTAKE_CLIP = 0xA85621D35cAf9Cf5C146D2376Ce553D7B78A6239; + address internal constant LOCKSTAKE_CLIP_CALC = 0xf13cF3b39823CcfaE6C2354dA56416C80768474e; + + // ---------- Wallets ---------- + address internal constant AAVE_V3_TREASURY = 0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c; + address internal constant EARLY_BIRD_REWARDS = 0x14D98650d46BF7679BBD05D4f615A1547C87Bf68; + + // ---------- Spark Proxy Spell ---------- + // Spark Proxy: https://github.com/marsfoundation/sparklend-deployments/blob/bba4c57d54deb6a14490b897c12a949aa035a99b/script/output/1/primary-sce-latest.json#L2 + address internal constant SPARK_PROXY = 0x3300f198988e4C9C63F75dF86De36421f06af8c4; + address internal constant SPARK_SPELL = 0xcc3B9e79261A7064A0f734Cc749A8e3762e0a187; + + function actions() public override { + // Note: multple actions in the spell depend on DssInstance + DssInstance memory dss = MCD.loadFromChainlog(DssExecLib.LOG); + + // ---------- Setup new MkrOsm ---------- + // Forum: https://forum.sky.money/t/atlas-weekly-cycle-edit-proposal-week-of-october-14-2024-01/25324 + // Poll: https://vote.makerdao.com/polling/QmUm8Krq + + // Whitelist MkrOsm to read from current PIP_MKR using `DssExecLib.addReaderToWhitelist` with the following parameters: + // Set parameter address _oracle: PIP_MKR address from chainlog (0xdbbe5e9b1daa91430cf0772fcebe53f6c6f137df) + // Set parameter address _reader: 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b + DssExecLib.addReaderToWhitelist(PIP_MKR, NEW_PIP_MKR); + + // Set MkrOsm as "PIP_MKR" in the chainlog using the following parameters: + // Set parameter bytes32 _key: "PIP_MKR" + // Set parameter address _val: 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b + DssExecLib.setChangelogAddress("PIP_MKR", NEW_PIP_MKR); + + // ---------- Setup new VoteDelegateFactory ---------- + // Forum: https://forum.sky.money/t/atlas-weekly-cycle-edit-proposal-week-of-october-14-2024-01/25324 + // Poll: https://vote.makerdao.com/polling/QmUm8Krq + + // Rename "VOTE_DELEGATE_PROXY_FACTORY" to "VOTE_DELEGATE_FACTORY_LEGACY" in chainlog: + // Note: this is a meta instruction, actual instructions are below + + // Call DssExecLib.setChangelogAddress with the following parameters: + // Set parameter bytes32 _key: "VOTE_DELEGATE_FACTORY_LEGACY" + // Set parameter address _val: VOTE_DELEGATE_PROXY_FACTORY address (0xd897f108670903d1d6070fcf818f9db3615af272) from the chainlog + DssExecLib.setChangelogAddress("VOTE_DELEGATE_FACTORY_LEGACY", VOTE_DELEGATE_PROXY_FACTORY); + + // Call CHAINLOG.removeAddress with the following parameters: + // Set parameter bytes32 _key: "VOTE_DELEGATE_PROXY_FACTORY" + dss.chainlog.removeAddress("VOTE_DELEGATE_PROXY_FACTORY"); + + // Set "VOTE_DELEGATE_FACTORY" in the chainlog to 0xC3D809E87A2C9da4F6d98fECea9135d834d6F5A0 + DssExecLib.setChangelogAddress("VOTE_DELEGATE_FACTORY", VOTE_DELEGATE_FACTORY); + + // ---------- Setup Lockstake Engine ---------- + // Forum: https://forum.sky.money/t/atlas-weekly-cycle-edit-proposal-week-of-october-14-2024-01/25324 + // Poll: https://vote.makerdao.com/polling/QmUm8Krq + + // SBE Parameter Changes + // Note: this is a subheading, actual instructions are below + + // Decrease splitter "burn" rate by 30% from 100% to 70% with the following parameters: + // Decrease splitter "burn" with address _base: MCD_SPLIT from chainlog + // Decrease splitter "burn" with bytes32 _what: "burn" + // Decrease splitter "burn" with uint256 _amt: 70% + DssExecLib.setValue(MCD_SPLIT, "burn", 70 * WAD / 100); + + // Increase vow.hump by 5 million DAI, from 55 million DAI to 60 million DAI + DssExecLib.setValue(MCD_VOW, "hump", 60 * MILLION * RAD); + + // Increase splitter.hop by 4,014 seconds, from 11,635 seconds to 15,649 seconds. + DssExecLib.setValue(MCD_SPLIT, "hop", 15_649); + + // Set Flapper farm by calling FlapperInit.setFarm with the following parameters: + FlapperInit.setFarm( + + // Note: FlapperInit.setFarm requires DssInstance + dss, + + // Set Flapper farm with address farm_ : 0x92282235a39bE957fF1f37619fD22A9aE5507CB1 + REWARDS_LSMKR_USDS, + + FarmConfig({ + // Set Flapper farm with address splitter: MCD_SPLIT from chainlog + splitter: MCD_SPLIT, + + // Set Flapper farm with address usdsJoin: USDS_JOIN from chainlog + usdsJoin: USDS_JOIN, + + // Set Flapper farm with uint256 hop: 15,649 + hop: 15_649 seconds, + + // Set Flapper farm with bytes32 prevChainlogKey: bytes32(0) + prevChainlogKey: bytes32(0), + + // Set Flapper farm with chainlogKey: "REWARDS_LSMKR_USDS" + chainlogKey: "REWARDS_LSMKR_USDS" + }) + ); + + // "Under the hood" actions for setting flapper: + // LsMkrUsdsFarm will be set as "farm" in MCD_SPLIT + // MCD_SPLIT will be set as "rewardsDistribution" in LsMkrUsdsFarm + // Provided "hop" will be set as "rewardsDuration" in LsMkrUsdsFarm + // New chainlog key REWARDS_LSMKR_USDS will be added + // Note: above instructions are taken inside FlapperInit.setFarm method + + // Note: prepare "farms" variable used inside Lockstake init call below + address[] memory farms = new address[](1); + farms[0] = REWARDS_LSMKR_USDS; + + // Init Lockstake Engine by calling LockstakeInit.initLockstake with the following parameters: + LockstakeInit.initLockstake( + + // Note: LockstakeInit.initLockstake requires DssInstance + dss, + + LockstakeInstance({ + // Init Lockstake Engine with address lsmkr: 0xb4e0e45e142101dC3Ed768bac219fC35EDBED295 + lsmkr: LOCKSTAKE_MKR, + + // Init Lockstake Engine with address engine: 0x2b16C07D5fD5cC701a0a871eae2aad6DA5fc8f12 + engine: LOCKSTAKE_ENGINE, + + // Init Lockstake Engine with address clipper: 0xA85621D35cAf9Cf5C146D2376Ce553D7B78A6239 + clipper: LOCKSTAKE_CLIP, + + // Init Lockstake Engine with address clipperCalc: 0xf13cF3b39823CcfaE6C2354dA56416C80768474e + clipperCalc: LOCKSTAKE_CLIP_CALC + }), + + LockstakeConfig({ + // Init Lockstake Engine with bytes32 ilk: "LSE-MKR-A" + ilk: "LSE-MKR-A", + + // Init Lockstake Engine with address voteDelegateFactory: 0xC3D809E87A2C9da4F6d98fECea9135d834d6F5A0 + voteDelegateFactory: VOTE_DELEGATE_FACTORY, + + // Init Lockstake Engine with address usdsJoin: USDS_JOIN from chainlog + usdsJoin: USDS_JOIN, + + // Init Lockstake Engine with address usds: USDS from chainlog + usds: USDS, + + // Init Lockstake Engine with address mkr: MCD_GOV from chainlog + mkr: MCD_GOV, + + // Init Lockstake Engine with address mkrSky: MKR_SKY from chainlog + mkrSky: MKR_SKY, + + // Init Lockstake Engine with address sky: SKY from chainlog + sky: SKY, + + // Init Lockstake Engine with address[] farms: 0x92282235a39bE957fF1f37619fD22A9aE5507CB1 + farms: farms, + + // Init Lockstake Engine with uint256 fee: 5% + fee: 5 * WAD / 100, + + // Init Lockstake Engine with uint256 maxLine: 20 million DAI + maxLine: 20 * MILLION * RAD, + + // Init Lockstake Engine with uint256 gap: 5 million + gap: 5 * MILLION * RAD, + + // Init Lockstake Engine with uint256 ttl: 16 hours + ttl: 16 hours, + + // Init Lockstake Engine with uint256 dust: 30,000 DAI + dust: 30_000 * RAD, + + // Init Lockstake Engine with uint256 duty: 12% + duty: TWELVE_PCT_RATE, + + // Init Lockstake Engine with uint256 mat: 200% + mat: 200 * RAY / 100, + + // Init Lockstake Engine with uint256 buf: 1.20 + buf: 120 * RAY / 100, + + // Init Lockstake Engine with uint256 tail: 6,000 seconds + tail: 6_000 seconds, + + // Init Lockstake Engine with uint256 cusp: 0.40 + cusp: 40 * RAY / 100, + + // Init Lockstake Engine with uint256 chip: 0.1% + chip: 1 * WAD / 1000, + + // Init Lockstake Engine with uint256 tip: 300 DAI + tip: 300 * RAD, + + // Init Lockstake Engine with uint256 stopped: 0 + stopped: 0, + + // Init Lockstake Engine with uint256 chop: 8% + chop: 108 * WAD / 100, + + // Init Lockstake Engine with uint256 hole: 3 million DAI + hole: 3 * MILLION * RAD, + + // Init Lockstake Engine with uint256 tau: 0 + tau: 0, + + // Init Lockstake Engine with uint256 cut: 0.99 + cut: 99 * RAY / 100, + + // Init Lockstake Engine with uint256 step: 60 seconds + step: 60 seconds, + + // Init Lockstake Engine with bool lineMom: true + lineMom: true, + + // Init Lockstake Engine with uint256 tolerance: 0.5 + tolerance: 5 * RAY / 10, + + // Init Lockstake Engine with string name: "LockstakeMkr" + name: "LockstakeMkr", + + // Init Lockstake Engine with string symbol: "lsMKR" + symbol: "lsMKR" + }) + ); + + // "Under the hood" actions for Init Lockstake Engine: + // New collateral type "LSE-MKR-A" will be added to "vat", "jug", "spotter", "dog" contracts + // New collateral type "LSE-MKR-A" will be added to LINE_MOM + // New collateral type "LSE-MKR-A" will be added to auto-line using provided maxLine, gap and ttl + // New collateral type "LSE-MKR-A" will be added to ILK_REGISTRY with provided values ("name", "symbol") and the new ilk class 7 + // The new MKR OSM will allow MCD_SPOT, CLIPPER_MOM, LOCKSTAKE_CLIP, MCD_END to access its price. + // PIP_MKR will be added to OSM_MOM + // LockstakeClipper will be configured using provided values ("buf", "tail", "cusp", "chip", "tip", "stopped", "clip", "tolerance") + // StairstepExponentialDecrease calc contract will be configured using provided values ("cut", "step") + // The LsMkrUsdsFarm will be added to the LockstakeEngine as a first farm + // LockstakeEngine will be authorized to access "vat" + // LockstakeClipper will be authorized to access "vat" and LockstakeEngine + // CLIPPER_MOM, MCD_DOG and MCD_END will be authorized to access LockstakeClipper + // New chainlog keys LOCKSTAKE_MKR, LOCKSTAKE_ENGINE, LOCKSTAKE_CLIP and LOCKSTAKE_CLIP_CALC will be added + // OSM_MOM will be authorized to access the new MKR OSM + // Note: above instructions are taken inside LockstakeInit.initLockstake method + + // ---------- Fund Early Bird Rewards Multisig ---------- + // Forum: https://forum.sky.money/t/atlas-weekly-cycle-edit-proposal-week-of-october-14-2024-01/25324#p-99402-early-bird-bonus-3 + // Poll: https://vote.makerdao.com/polling/QmUm8Krq + + // Mint 27,222,832.80 SKY to 0x14D98650d46BF7679BBD05D4f615A1547C87Bf68 + SkyLike(SKY).mint(EARLY_BIRD_REWARDS, 27_222_832.80 ether); // Note: ether is only a keyword helper + + // ---------- Lower Deprecated RWA Debt Ceilings ---------- + // Forum: https://forum.sky.money/t/2024-10-17-expected-executive-contents-rwa-vault-changes/25323 + + // Remove RWA007-A from Debt Ceiling Instant Access Module + DssExecLib.removeIlkFromAutoLine("RWA007-A"); + + // Note: in order to decrease global debt ceiling, we need to fetch current `line` + (,,, uint256 line1,) = VatAbstract(MCD_VAT).ilks("RWA007-A"); + + // Set RWA007-A Debt Ceiling to 0 + DssExecLib.setIlkDebtCeiling("RWA007-A", 0); + + // Initiate RWA007-A soft liquidation by calling `tell()` + RwaLiquidationOracleLike(MIP21_LIQUIDATION_ORACLE).tell("RWA007-A"); + + // Write-off the debt of RWA007-A and set its oracle price to 0 by calling `cull()` + RwaLiquidationOracleLike(MIP21_LIQUIDATION_ORACLE).cull("RWA007-A", RWA007_A_URN); + + // Note: update the spot value in vat by propagating the price + DssExecLib.updateCollateralPrice("RWA007-A"); + + // Note: in order to decrease global debt ceiling, we need to fetch current `line` + (,,, uint256 line2,) = VatAbstract(MCD_VAT).ilks("RWA014-A"); + + // Reduce RWA014-A Debt Ceiling by 1.5 billion Dai from 1.5 billion Dai to 0 + DssExecLib.setIlkDebtCeiling("RWA014-A", 0); + + // Initiate RWA014-A soft liquidation by calling `tell()` + RwaLiquidationOracleLike(MIP21_LIQUIDATION_ORACLE).tell("RWA014-A"); + + // Write-off the debt of RWA014-A and set its oracle price to 0 by calling `cull()` + RwaLiquidationOracleLike(MIP21_LIQUIDATION_ORACLE).cull("RWA014-A", RWA014_A_URN); + + // Note: update the spot value in vat by propagating the price + DssExecLib.updateCollateralPrice("RWA014-A"); + + // Note: decrease global line + VatAbstract(MCD_VAT).file("Line", VatAbstract(MCD_VAT).Line() - (line1 + line2)); + + // ---------- Pinwheel DAO Resolution ---------- + // Forum: https://forum.sky.money/t/coinbase-web3-wallet-legal-overview/24577/3 + + // Approve DAO Resolution at QmYJUvw5xbAJmJknG2xUKDLe424JSTWQQhbJCnucRRjUv7 + // Note: see `dao_resolutions` public variable declared above + + // ---------- AAVE Revenue Share Payment ---------- + // Forum: https://forum.sky.money/t/spark-aave-revenue-share-calculation-payment-5-q3-2024/25286 + + // AAVE Revenue Share - 234089 DAI - 0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c + DssExecLib.sendPaymentFromSurplusBuffer(AAVE_V3_TREASURY, 234_089); + + // ---------- Spark Spell ---------- + // Forum: https://forum.sky.money/t/oct-3-2024-proposed-changes-to-spark-for-upcoming-spell/25293 + // Poll: https://vote.makerdao.com/polling/QmbHaA2G + // Poll: https://vote.makerdao.com/polling/QmShWccA + // Poll: https://vote.makerdao.com/polling/QmTksxrr + + // Execute Spark Proxy Spell at 0xcc3B9e79261A7064A0f734Cc749A8e3762e0a187 + ProxyLike(SPARK_PROXY).exec(SPARK_SPELL, abi.encodeWithSignature("execute()")); + + // ---------- Chainlog bump ---------- + + // Note: we have to patch chainlog version as new collateral is added + DssExecLib.setChangelogVersion("1.19.2"); + } +} + +contract DssSpell is DssExec { + constructor() DssExec(block.timestamp + 30 days, address(new DssSpellAction())) {} +} diff --git a/archive/2024-10-17-DssSpell/DssSpell.t.base.sol b/archive/2024-10-17-DssSpell/DssSpell.t.base.sol new file mode 100644 index 000000000..b68430b99 --- /dev/null +++ b/archive/2024-10-17-DssSpell/DssSpell.t.base.sol @@ -0,0 +1,3178 @@ +// SPDX-FileCopyrightText: © 2020 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity 0.8.16; + +import "dss-interfaces/Interfaces.sol"; +import {DssTest, GodMode} from "dss-test/DssTest.sol"; +import {stdStorage, StdStorage} from "forge-std/Test.sol"; + +import "./test/rates.sol"; +import "./test/addresses_mainnet.sol"; +import "./test/addresses_deployers.sol"; +import "./test/addresses_wallets.sol"; +import "./test/config.sol"; + +import {DssSpell} from "./DssSpell.sol"; + +struct TeleportGUID { + bytes32 sourceDomain; + bytes32 targetDomain; + bytes32 receiver; + bytes32 operator; + uint128 amount; + uint80 nonce; + uint48 timestamp; +} + +interface DirectDepositLike is GemJoinAbstract { + function file(bytes32, uint256) external; + function exec() external; + function tau() external view returns (uint256); + function tic() external view returns (uint256); + function bar() external view returns (uint256); + function king() external view returns (address); +} + +interface AaveDirectDepositLike is DirectDepositLike { + function adai() external view returns (address); +} + +interface CropperLike { + function getOrCreateProxy(address usr) external returns (address urp); + function join(address crop, address usr, uint256 val) external; + function exit(address crop, address usr, uint256 val) external; + function frob(bytes32 ilk, address u, address v, address w, int256 dink, int256 dart) external; +} + +interface CropJoinLike { + function wards(address) external view returns (uint256); + function gem() external view returns (address); + function bonus() external view returns (address); +} + +interface CurveLPOsmLike is LPOsmAbstract { + function orbs(uint256) external view returns (address); +} + +interface TeleportJoinLike { + function wards(address) external view returns (uint256); + function fees(bytes32) external view returns (address); + function line(bytes32) external view returns (uint256); + function debt(bytes32) external view returns (int256); + function vow() external view returns (address); + function vat() external view returns (address); + function daiJoin() external view returns (address); + function ilk() external view returns (bytes32); + function domain() external view returns (bytes32); +} + +interface TeleportFeeLike { + function fee() external view returns (uint256); + function ttl() external view returns (uint256); +} + +interface TeleportOracleAuthLike { + function wards(address) external view returns (uint256); + function signers(address) external view returns (uint256); + function teleportJoin() external view returns (address); + function threshold() external view returns (uint256); + function addSigners(address[] calldata) external; + function getSignHash(TeleportGUID calldata) external pure returns (bytes32); + function requestMint( + TeleportGUID calldata, + bytes calldata, + uint256, + uint256 + ) external returns (uint256, uint256); +} + +interface TeleportRouterLike { + function wards(address) external view returns (uint256); + function file(bytes32, bytes32, address) external; + function gateways(bytes32) external view returns (address); + function domains(address) external view returns (bytes32); + function numDomains() external view returns (uint256); + function dai() external view returns (address); + function requestMint( + TeleportGUID calldata, + uint256, + uint256 + ) external returns (uint256, uint256); + function settle(bytes32, uint256) external; +} + +interface TeleportBridgeLike { + function l1Escrow() external view returns (address); + function l1TeleportRouter() external view returns (address); + function l1Token() external view returns (address); +} + +interface OptimismTeleportBridgeLike is TeleportBridgeLike { + function l2TeleportGateway() external view returns (address); + function messenger() external view returns (address); +} + +interface ArbitrumTeleportBridgeLike is TeleportBridgeLike { + function l2TeleportGateway() external view returns (address); + function inbox() external view returns (address); +} + +interface StarknetTeleportBridgeLike { + function l2TeleportGateway() external view returns (uint256); + function starkNet() external view returns (address); +} + +interface RwaLiquidationLike { + function ilks(bytes32) external view returns (string memory, address, uint48, uint48); +} + +interface AuthorityLike { + function authority() external view returns (address); +} + +interface SplitterMomLike { + function authority() external view returns (address); + function stop() external; +} + +// TODO: add full interfaces to dss-interfaces and remove from here +interface FlapUniV2Like { + function gem() external view returns (address); + function pair() external view returns (address); + function pip() external view returns (address); + function want() external view returns (uint256); +} + +// TODO: add full interfaces to dss-interfaces and remove from here +interface SplitLike { + function burn() external view returns (uint256); + function farm() external view returns (address); + function flapper() external view returns (address); + function hop() external view returns (uint256); +} + +interface FlapOracleLike { + function read() external view returns (bytes32); +} + +// TODO: add full interfaces to dss-interfaces and remove from here +interface UsdsJoinLike is DaiJoinAbstract {} + +interface SUsdsLike { + function allowance(address, address) external view returns (uint256); + function approve(address spender, uint256 value) external returns (bool); + function asset() external view returns (address); + function balanceOf(address) external view returns (uint256); + function chi() external view returns (uint192); + function convertToAssets(uint256 shares) external view returns (uint256); + function convertToShares(uint256 assets) external view returns (uint256); + function deposit(uint256 assets, address receiver) external returns (uint256 shares); + function drip() external returns (uint256 nChi); + function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); + function rho() external view returns (uint64); + function ssr() external view returns (uint256); +} + +interface DaiUsdsLike { + function daiToUsds(address usr, uint256 wad) external; + function usdsToDai(address usr, uint256 wad) external; +} + +interface MkrSkyLike { + function mkrToSky(address usr, uint256 mkrAmt) external; + function skyToMkr(address usr, uint256 skyAmt) external; +} + +interface LitePsmLike { + function bud(address) external view returns (uint256); + function buf() external view returns (uint256); + function buyGem(address usr, uint256 gemAmt) external returns (uint256 daiInWad); + function buyGemNoFee(address usr, uint256 gemAmt) external returns (uint256 daiInWad); + function chug() external returns (uint256 wad); + function cut() external view returns (uint256 wad); + function daiJoin() external view returns (address); + function file(bytes32 what, uint256 data) external; + function fill() external returns (uint256 wad); + function gem() external view returns (address); + function gemJoin() external view returns (address); + function gush() external view returns (uint256 wad); + function ilk() external view returns (bytes32); + function kiss(address usr) external; + function pocket() external view returns (address); + function rush() external view returns (uint256 wad); + function sellGem(address usr, uint256 gemAmt) external returns (uint256 daiOutWad); + function sellGemNoFee(address usr, uint256 gemAmt) external returns (uint256 daiOutWad); + function tin() external view returns (uint256); + function to18ConversionFactor() external view returns (uint256); + function tout() external view returns (uint256); + function trim() external returns (uint256 wad); + function vow() external view returns (address); + function wards(address) external view returns (uint256); +} + +interface LitePsmMomLike is AuthorityLike { + function halt(address, uint8) external; +} + +interface StakingRewardsLike { + function stake(uint256 amount) external; + function notifyRewardAmount(uint256 reward) external; + function rewardPerToken() external view returns (uint256); + function rewardsDistribution() external view returns (address); + function rewardsDuration() external view returns (uint256); + function rewardsToken() external view returns (address); + function stakingToken() external view returns (address); +} + +interface LockstakeEngineLike { + function addFarm(address farm) external; + function delFarm(address farm) external; + function deny(address usr) external; + function draw(address owner, uint256 index, address to, uint256 wad) external; + function farms(address farm) external view returns (uint8 farmStatus); + function fee() external view returns (uint256); + function file(bytes32 what, uint256 data) external; + function file(bytes32 what, address data) external; + function free(address owner, uint256 index, address to, uint256 wad) external returns (uint256 freed); + function freeNoFee(address owner, uint256 index, address to, uint256 wad) external; + function freeSky(address owner, uint256 index, address to, uint256 skyWad) external returns (uint256 skyFreed); + function getReward(address owner, uint256 index, address farm, address to) external returns (uint256 amt); + function hope(address owner, uint256 index, address usr) external; + function ilk() external view returns (bytes32); + function isUrnAuth(address owner, uint256 index, address usr) external view returns (bool ok); + function jug() external view returns (address); + function lock(address owner, uint256 index, uint256 wad, uint16 ref) external; + function lockSky(address owner, uint256 index, uint256 skyWad, uint16 ref) external; + function lsmkr() external view returns (address); + function mkr() external view returns (address); + function mkrSky() external view returns (address); + function mkrSkyRate() external view returns (uint256); + function multicall(bytes[] memory data) external returns (bytes[] memory results); + function nope(address owner, uint256 index, address usr) external; + function onKick(address urn, uint256 wad) external; + function onRemove(address urn, uint256 sold, uint256 left) external; + function onTake(address urn, address who, uint256 wad) external; + function open(uint256 index) external returns (address urn); + function ownerUrns(address owner, uint256 index) external view returns (address urn); + function ownerUrnsCount(address owner) external view returns (uint256 count); + function rely(address usr) external; + function selectFarm(address owner, uint256 index, address farm, uint16 ref) external; + function selectVoteDelegate(address owner, uint256 index, address voteDelegate) external; + function sky() external view returns (address); + function urnAuctions(address urn) external view returns (uint256 auctionsCount); + function urnCan(address urn, address usr) external view returns (uint256 allowed); + function urnFarms(address urn) external view returns (address farm); + function urnImplementation() external view returns (address); + function urnOwners(address urn) external view returns (address owner); + function urnVoteDelegates(address urn) external view returns (address voteDelegate); + function usds() external view returns (address); + function usdsJoin() external view returns (address); + function vat() external view returns (address); + function voteDelegateFactory() external view returns (address); + function wards(address usr) external view returns (uint256 allowed); + function wipe(address owner, uint256 index, uint256 wad) external; + function wipeAll(address owner, uint256 index) external returns (uint256 wad); +} + +interface LockstakeClipperLike { + function vat() external view returns (address); + function dog() external view returns (address); + function spotter() external view returns (address); + function engine() external view returns (address); + function ilk() external view returns (bytes32); + function rely(address) external; + function file(bytes32, address) external; + function file(bytes32, uint256) external; + function upchost() external; + function sales(uint256) + external + view + returns (uint256 pos, uint256 tab, uint256 lot, uint256 tot, address usr, uint96 tic, uint256 top); + function stopped() external view returns (uint256); +} + +interface VoteDelegateFactoryLike { + function create() external returns (address voteDelegate); +} + +contract DssSpellTestBase is Config, DssTest { + using stdStorage for StdStorage; + + Rates rates = new Rates(); + Addresses addr = new Addresses(); + Deployers deployers = new Deployers(); + Wallets wallets = new Wallets(); + + // ADDRESSES + ChainlogAbstract chainLog = ChainlogAbstract( addr.addr("CHANGELOG")); + DSPauseAbstract pause = DSPauseAbstract( addr.addr("MCD_PAUSE")); + address pauseProxy = addr.addr("MCD_PAUSE_PROXY"); + DSChiefAbstract chief = DSChiefAbstract( addr.addr("MCD_ADM")); + VatAbstract vat = VatAbstract( addr.addr("MCD_VAT")); + VowAbstract vow = VowAbstract( addr.addr("MCD_VOW")); + DogAbstract dog = DogAbstract( addr.addr("MCD_DOG")); + PotAbstract pot = PotAbstract( addr.addr("MCD_POT")); + JugAbstract jug = JugAbstract( addr.addr("MCD_JUG")); + SpotAbstract spotter = SpotAbstract( addr.addr("MCD_SPOT")); + DaiAbstract dai = DaiAbstract( addr.addr("MCD_DAI")); + DaiJoinAbstract daiJoin = DaiJoinAbstract( addr.addr("MCD_JOIN_DAI")); + GemAbstract usds = GemAbstract( addr.addr("USDS")); + SUsdsLike susds = SUsdsLike( addr.addr("SUSDS")); + UsdsJoinLike usdsJoin = UsdsJoinLike( addr.addr("USDS_JOIN")); + DSTokenAbstract gov = DSTokenAbstract( addr.addr("MCD_GOV")); + DSTokenAbstract mkr = DSTokenAbstract( addr.addr("MCD_GOV")); + GemAbstract sky = GemAbstract( addr.addr("SKY")); + MkrSkyLike mkrSky = MkrSkyLike( addr.addr("MKR_SKY")); + EndAbstract end = EndAbstract( addr.addr("MCD_END")); + ESMAbstract esm = ESMAbstract( addr.addr("MCD_ESM")); + CureAbstract cure = CureAbstract( addr.addr("MCD_CURE")); + IlkRegistryAbstract reg = IlkRegistryAbstract(addr.addr("ILK_REGISTRY")); + SplitLike split = SplitLike( addr.addr("MCD_SPLIT")); + FlapUniV2Like flap = FlapUniV2Like( addr.addr("MCD_FLAP")); + CropperLike cropper = CropperLike( addr.addr("MCD_CROPPER")); + + OsmMomAbstract osmMom = OsmMomAbstract( addr.addr("OSM_MOM")); + ClipperMomAbstract clipMom = ClipperMomAbstract( addr.addr("CLIPPER_MOM")); + AuthorityLike d3mMom = AuthorityLike( addr.addr("DIRECT_MOM")); + AuthorityLike lineMom = AuthorityLike( addr.addr("LINE_MOM")); + AuthorityLike litePsmMom = AuthorityLike( addr.addr("LITE_PSM_MOM")); + SplitterMomLike splitterMom = SplitterMomLike( addr.addr("SPLITTER_MOM")); + DssAutoLineAbstract autoLine = DssAutoLineAbstract(addr.addr("MCD_IAM_AUTO_LINE")); + LerpFactoryAbstract lerpFactory = LerpFactoryAbstract(addr.addr("LERP_FAB")); + VestAbstract vestDai = VestAbstract( addr.addr("MCD_VEST_DAI")); + VestAbstract vestMkr = VestAbstract( addr.addr("MCD_VEST_MKR_TREASURY")); + VestAbstract vestSky = VestAbstract( addr.addr("MCD_VEST_SKY")); + RwaLiquidationLike liquidationOracle = RwaLiquidationLike( addr.addr("MIP21_LIQUIDATION_ORACLE")); + address voteDelegateFactory = addr.addr("VOTE_DELEGATE_FACTORY"); + + DssSpell spell; + + event Debug(uint256 index, uint256 val); + event Debug(uint256 index, address addr); + event Debug(uint256 index, bytes32 what); + + function _rmul(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = (x * y + RAY / 2) / RAY; + } + + // not provided in DSMath + function _rpow(uint256 x, uint256 n, uint256 b) internal pure returns (uint256 z) { + assembly { + switch x case 0 {switch n case 0 {z := b} default {z := 0}} + default { + switch mod(n, 2) case 0 { z := b } default { z := x } + let half := div(b, 2) // for rounding. + for { n := div(n, 2) } n { n := div(n,2) } { + let xx := mul(x, x) + if iszero(eq(div(xx, x), x)) { revert(0,0) } + let xxRound := add(xx, half) + if lt(xxRound, xx) { revert(0,0) } + x := div(xxRound, b) + if mod(n,2) { + let zx := mul(z, x) + if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0,0) } + let zxRound := add(zx, half) + if lt(zxRound, zx) { revert(0,0) } + z := div(zxRound, b) + } + } + } + } + } + + function _divup(uint256 x, uint256 y) internal pure returns (uint256 z) { + unchecked { + z = x != 0 ? ((x - 1) / y) + 1 : 0; + } + } + + // not provided in DSTest + function _assertEqApprox(uint256 _a, uint256 _b, uint256 _tolerance) internal { + uint256 a = _a; + uint256 b = _b; + if (a < b) { + uint256 tmp = a; + a = b; + b = tmp; + } + if (a - b > _tolerance) { + emit log_bytes32("Error: Wrong `uint' value"); + emit log_named_uint(" Expected", _b); + emit log_named_uint(" Actual", _a); + fail(); + } + } + + function _cmpStr(string memory a, string memory b) internal pure returns (bool) { + return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)))); + } + + function _concat(string memory a, string memory b) internal pure returns (string memory) { + return string.concat(a, b); + } + + function _concat(string memory a, bytes32 b) internal pure returns (string memory) { + return string.concat(a, _bytes32ToString(b)); + } + + function _bytes32ToString(bytes32 _bytes32) internal pure returns (string memory) { + uint256 charCount = 0; + while(charCount < 32 && _bytes32[charCount] != 0) { + charCount++; + } + bytes memory bytesArray = new bytes(charCount); + for (uint256 i = 0; i < charCount; i++) { + bytesArray[i] = _bytes32[i]; + } + return string(bytesArray); + } + + function _stringToBytes32(string memory source) internal pure returns (bytes32 result) { + assembly { + result := mload(add(source, 32)) + } + } + + /** + * @dev Add this modifier to a test to skip it. + * It will still show in the test report, but with a `[SKIP]` label added to it. + * This is meant to be used for tests that need to be enabled/disabled on-demand. + */ + modifier skipped() { + vm.skip(true); + _; + } + + modifier skippedWhenDeployed() { + if (spellValues.deployed_spell != address(0)) { + vm.skip(true); + } + _; + } + + modifier skippedWhenNotDeployed() { + if (spellValues.deployed_spell == address(0)) { + vm.skip(true); + } + _; + } + + // 10^-5 (tenth of a basis point) as a RAY + uint256 TOLERANCE = 10 ** 22; + + function _yearlyYield(uint256 duty) internal pure returns (uint256) { + return _rpow(duty, (365 * 24 * 60 * 60), RAY); + } + + function _expectedRate(uint256 percentValue) internal pure returns (uint256) { + return (10000 + percentValue) * (10 ** 23); + } + + function _diffCalc( + uint256 expectedRate_, + uint256 yearlyYield_ + ) internal pure returns (uint256) { + return (expectedRate_ > yearlyYield_) ? + expectedRate_ - yearlyYield_ : yearlyYield_ - expectedRate_; + } + + function _castPreviousSpell() internal { + address[] memory prevSpells = spellValues.previous_spells; + + // warp and cast previous spells so values are up-to-date to test against + for (uint256 i; i < prevSpells.length; i++) { + DssSpell prevSpell = DssSpell(prevSpells[i]); + if (prevSpell != DssSpell(address(0)) && !prevSpell.done()) { + if (prevSpell.eta() == 0) { + _vote(address(prevSpell)); + _scheduleWaitAndCast(address(prevSpell)); + } + else { + // jump to nextCastTime to be a little more forgiving on the spell execution time + vm.warp(prevSpell.nextCastTime()); + prevSpell.cast(); + } + } + } + } + + function setUp() public { + setValues(); + _castPreviousSpell(); + + 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) + ); + } + } + + function _vote(address spell_) internal { + if (chief.hat() != spell_) { + _giveTokens(address(gov), 999999999999 ether); + gov.approve(address(chief), type(uint256).max); + chief.lock(999999999999 ether); + + address[] memory slate = new address[](1); + + assertFalse(DssSpell(spell_).done(), "TestError/spell-done-before-vote"); + + slate[0] = spell_; + + chief.vote(slate); + chief.lift(spell_); + } + assertEq(chief.hat(), spell_); + } + + function _scheduleWaitAndCast(address spell_) internal { + DssSpell(spell_).schedule(); + + vm.warp(DssSpell(spell_).nextCastTime()); + + DssSpell(spell_).cast(); + } + + function _checkSystemValues(SystemValues storage values) internal { + // dsr + uint256 expectedDSRRate = rates.rates(values.pot_dsr); + // make sure dsr is less than 100% APR + // bc -l <<< 'scale=27; e( l(2.00)/(60 * 60 * 24 * 365) )' + // 1000000021979553151239153027 + assertEq(pot.dsr(), expectedDSRRate, "TestError/pot-dsr-expected-value"); + assertTrue( + pot.dsr() >= RAY && pot.dsr() < 1000000021979553151239153027, + "TestError/pot-dsr-range" + ); + assertTrue( + _diffCalc(_expectedRate(values.pot_dsr), _yearlyYield(expectedDSRRate)) <= TOLERANCE, + "TestError/pot-dsr-rates-table" + ); + + // ssr + uint256 expectedSSRRate = rates.rates(values.susds_ssr); + // make sure dsr is less than 100% APR + // bc -l <<< 'scale=27; e( l(2.00)/(60 * 60 * 24 * 365) )' + // 1000000021979553151239153027 + assertEq(susds.ssr(), expectedSSRRate, "TestError/susds-ssr-expected-value"); + assertTrue( + susds.ssr() >= RAY && susds.ssr() < 1000000021979553151239153027, + "TestError/susds-ssr-range" + ); + assertTrue( + _diffCalc(_expectedRate(values.susds_ssr), _yearlyYield(expectedSSRRate)) <= TOLERANCE, + "TestError/susds-ssr-rates-table" + ); + // SSR should always be higher than or equal to DSR + assertGe(expectedSSRRate, expectedDSRRate, "TestError/ssr-lower-than-dsr"); + + { + // Line values in RAD + assertTrue( + (vat.Line() >= RAD && vat.Line() < 100 * BILLION * RAD) || + vat.Line() == 0, + "TestError/vat-Line-range" + ); + } + + // Pause delay + assertEq(pause.delay(), values.pause_delay, "TestError/pause-delay"); + + // wait + assertEq(vow.wait(), values.vow_wait, "TestError/vow-wait"); + { + // dump values in WAD + uint256 normalizedDump = values.vow_dump * WAD; + assertEq(vow.dump(), normalizedDump, "TestError/vow-dump"); + assertTrue( + (vow.dump() >= WAD && vow.dump() < 2 * THOUSAND * WAD) || + vow.dump() == 0, + "TestError/vow-dump-range" + ); + } + { + // sump values in RAD + uint256 normalizedSump = values.vow_sump * RAD; + assertEq(vow.sump(), normalizedSump, "TestError/vow-sump"); + assertTrue( + (vow.sump() >= RAD && vow.sump() < 500 * THOUSAND * RAD) || + vow.sump() == 0, + "TestError/vow-sump-range" + ); + } + { + // bump values in RAD + uint256 normalizedBump = values.vow_bump * RAD; + assertEq(vow.bump(), normalizedBump, "TestError/vow-bump"); + assertTrue( + (vow.bump() >= RAD && vow.bump() < 100 * THOUSAND * RAD) || + vow.bump() == 0, + "TestError/vow-bump-range" + ); + } + { + // hump values in RAD + uint256 normalizedHumpMin = values.vow_hump_min * RAD; + uint256 normalizedHumpMax = values.vow_hump_max * RAD; + assertTrue(vow.hump() >= normalizedHumpMin && vow.hump() <= normalizedHumpMax, "TestError/vow-hump-min-max"); + assertTrue( + (vow.hump() >= RAD && vow.hump() < 1 * BILLION * RAD) || + vow.hump() == 0, + "TestError/vow-hump-range" + ); + } + + // Hole value in RAD + { + uint256 normalizedHole = values.dog_Hole * RAD; + assertEq(dog.Hole(), normalizedHole, "TestError/dog-Hole"); + assertTrue(dog.Hole() >= MILLION * RAD && dog.Hole() <= 200 * MILLION * RAD, "TestError/dog-Hole-range"); + } + + // ESM min in WAD + { + uint256 normalizedMin = values.esm_min * WAD; + assertEq(esm.min(), normalizedMin, "TestError/esm-min"); + assertTrue(esm.min() > WAD && esm.min() < 400 * THOUSAND * WAD, "TestError/esm-min-range"); + } + + // check Pause authority + assertEq(pause.authority(), addr.addr(values.pause_authority), "TestError/pause-authority"); + + // check OsmMom authority + assertEq(osmMom.authority(), addr.addr(values.osm_mom_authority), "TestError/osmMom-authority"); + + // check ClipperMom authority + assertEq(clipMom.authority(), addr.addr(values.clipper_mom_authority), "TestError/clipperMom-authority"); + + // check D3MMom authority + assertEq(d3mMom.authority(), addr.addr(values.d3m_mom_authority), "TestError/d3mMom-authority"); + + // check LineMom authority + assertEq(lineMom.authority(), addr.addr(values.line_mom_authority), "TestError/lineMom-authority"); + + // check LitePsmMom authority + assertEq(litePsmMom.authority(), addr.addr(values.lite_psm_mom_authority), "TestError/linePsmMom-authority"); + + // check SplitterMom authority + assertEq(splitterMom.authority(), addr.addr(values.splitter_mom_authority), "TestError/splitterMom-authority"); + + // check number of ilks + assertEq(reg.count(), values.ilk_count, "TestError/ilks-count"); + + // split + { + // check split hop and sanity checks + assertEq(split.hop(), values.split_hop, "TestError/split-hop"); + assertTrue(split.hop() > 0 && split.hop() < 86400, "TestError/split-hop-range"); // gt 0 && lt 1 day + // check burn value + uint256 normalizedTestBurn = values.split_burn * 10**14; + assertEq(split.burn(), normalizedTestBurn, "TestError/split-burn"); + assertTrue(split.burn() >= 50 * WAD / 100 && split.burn() <= 1 * WAD, "TestError/split-burn-range"); // gte 50% and lte 100% + // check split.farm address to match config + address split_farm = addr.addr(values.split_farm); + assertEq(split.farm(), split_farm, "TestError/split-farm"); + // check farm rewards distribution and duration to match splitter + if (split_farm != address(0)) { + address rewardsDistribution = StakingRewardsLike(split_farm).rewardsDistribution(); + assertEq(rewardsDistribution, address(split), "TestError/farm-distribution"); + uint256 rewardsDuration = StakingRewardsLike(split_farm).rewardsDuration(); + assertEq(rewardsDuration, values.split_hop, "TestError/farm-duration-does-not-match-split-hop"); + } + } + + // flap + { + // check want value + uint256 normalizedTestWant = values.flap_want * 10**14; + assertEq(flap.want(), normalizedTestWant, "TestError/flap-want"); + assertTrue(flap.want() >= 90 * WAD / 100 && flap.want() <= 110 * WAD / 100, "TestError/flap-want-range"); // gte 90% and lte 110% + } + + // vest + { + assertEq(vestDai.cap(), values.vest_dai_cap, "TestError/vest-dai-cap"); + assertEq(vestMkr.cap(), values.vest_mkr_cap, "TestError/vest-mkr-cap"); + assertEq(vestSky.cap(), values.vest_sky_cap, "TestError/vest-sky-cap"); + } + + assertEq(vat.wards(pauseProxy), uint256(1), "TestError/pause-proxy-deauthed-on-vat"); + + // transferrable vest + // check mkr allowance + _checkTransferrableVestMkrAllowance(); + } + + function _checkCollateralValues(SystemValues storage values) internal { + // Using an array to work around stack depth limitations. + // sums[0] : sum of all lines + // sums[1] : sum over ilks of (line - Art * rate)--i.e. debt that could be drawn at any time + uint256[] memory sums = new uint256[](2); + bytes32[] memory ilks = reg.list(); + for(uint256 i = 0; i < ilks.length; i++) { + bytes32 ilk = ilks[i]; + (uint256 duty,) = jug.ilks(ilk); + + assertEq(duty, rates.rates(values.collaterals[ilk].pct), _concat("TestError/jug-duty-", ilk)); + // make sure duty is less than 1000% APR + // bc -l <<< 'scale=27; e( l(10.00)/(60 * 60 * 24 * 365) )' + // 1000000073014496989316680335 + assertTrue(duty >= RAY && duty < 1000000073014496989316680335, _concat("TestError/jug-duty-range-", ilk)); // gt 0 and lt 1000% + assertTrue( + _diffCalc(_expectedRate(values.collaterals[ilk].pct), _yearlyYield(rates.rates(values.collaterals[ilk].pct))) <= TOLERANCE, + _concat("TestError/rates-", ilk) + ); + assertTrue(values.collaterals[ilk].pct < THOUSAND * THOUSAND, _concat("TestError/pct-max-", ilk)); // check value lt 1000% + { + uint256 line; + uint256 dust; + { + uint256 Art; + uint256 rate; + (Art, rate,, line, dust) = vat.ilks(ilk); + if (Art * rate < line) { + sums[1] += line - Art * rate; + } + } + // Convert whole Dai units to expected RAD + uint256 normalizedTestLine = values.collaterals[ilk].line * RAD; + sums[0] += line; + (uint256 aL_line, uint256 aL_gap, uint256 aL_ttl,,) = autoLine.ilks(ilk); + if (!values.collaterals[ilk].aL_enabled) { + assertTrue(aL_line == 0, _concat("TestError/al-Line-not-zero-", ilk)); + assertEq(line, normalizedTestLine, _concat("TestError/vat-line-", ilk)); + assertTrue((line >= RAD && line < 10 * BILLION * RAD) || line == 0, _concat("TestError/vat-line-range-", ilk)); // eq 0 or gt eq 1 RAD and lt 10B + } else { + assertTrue(aL_line > 0, _concat("TestError/al-Line-is-zero-", ilk)); + assertEq(aL_line, values.collaterals[ilk].aL_line * RAD, _concat("TestError/al-line-", ilk)); + assertEq(aL_gap, values.collaterals[ilk].aL_gap * RAD, _concat("TestError/al-gap-", ilk)); + assertEq(aL_ttl, values.collaterals[ilk].aL_ttl, _concat("TestError/al-ttl-", ilk)); + assertTrue((aL_line >= RAD && aL_line < 20 * BILLION * RAD) || aL_line == 0, _concat("TestError/al-line-range-", ilk)); // eq 0 or gt eq 1 RAD and lt 10B + } + uint256 normalizedTestDust = values.collaterals[ilk].dust * RAD; + assertEq(dust, normalizedTestDust, _concat("TestError/vat-dust-", ilk)); + assertTrue((dust >= RAD && dust <= 100 * THOUSAND * RAD) || dust == 0, _concat("TestError/vat-dust-range-", ilk)); // eq 0 or gt eq 1 and lte 100k + } + + { + (address pip, uint256 mat) = spotter.ilks(ilk); + if (pip != address(0)) { + // Convert BP to system expected value + uint256 normalizedTestMat = (values.collaterals[ilk].mat * 10**23); + if (values.collaterals[ilk].offboarding) { + assertTrue(mat <= normalizedTestMat, _concat("TestError/vat-lerping-mat-", ilk)); + assertTrue(mat >= RAY && mat <= 300 * RAY, _concat("TestError/vat-mat-range-", ilk)); // cr gt 100% and lt 30000% + } else { + assertEq(mat, normalizedTestMat, _concat("TestError/vat-mat-", ilk)); + assertTrue(mat >= RAY && mat < 10 * RAY, _concat("TestError/vat-mat-range-", ilk)); // cr gt 100% and lt 1000% + } + } + } + + if (values.collaterals[ilk].liqType == "flip") { + // NOTE: MCD_CAT has been scuttled in the spell on 2023-09-13 + revert("TestError/flip-deprecated"); + } + if (values.collaterals[ilk].liqType == "clip") { + { + assertTrue(reg.class(ilk) == 1 || reg.class(ilk) == 7, _concat("TestError/reg-class-", ilk)); + (bool ok, bytes memory val) = reg.xlip(ilk).call(abi.encodeWithSignature("dog()")); + assertTrue(ok, _concat("TestError/reg-xlip-dog-", ilk)); + assertEq(abi.decode(val, (address)), address(dog), _concat("TestError/reg-xlip-dog-", ilk)); + } + { + (, uint256 chop, uint256 hole,) = dog.ilks(ilk); + // Convert BP to system expected value + uint256 normalizedTestChop = (values.collaterals[ilk].chop * 10**14) + WAD; + assertEq(chop, normalizedTestChop, _concat("TestError/dog-chop-", ilk)); + // make sure chop is less than 100% + assertTrue(chop >= WAD && chop < 2 * WAD, _concat("TestError/dog-chop-range-", ilk)); // penalty gt eq 0% and lt 100% + + // Convert whole Dai units to expected RAD + uint256 normalizedTesthole = values.collaterals[ilk].dog_hole * RAD; + assertEq(hole, normalizedTesthole, _concat("TestError/dog-hole-", ilk)); + assertTrue(hole == 0 || hole >= RAD && hole <= 100 * MILLION * RAD, _concat("TestError/dog-hole-range-", ilk)); + } + (address clipper,,,) = dog.ilks(ilk); + assertTrue(clipper != address(0), _concat("TestError/invalid-clip-address-", ilk)); + ClipAbstract clip = ClipAbstract(clipper); + { + // Convert BP to system expected value + uint256 normalizedTestBuf = values.collaterals[ilk].clip_buf * 10**23; + assertEq(uint256(clip.buf()), normalizedTestBuf, _concat("TestError/clip-buf-", ilk)); + assertTrue(clip.buf() >= RAY && clip.buf() <= 2 * RAY, _concat("TestError/clip-buf-range-", ilk)); // gte 0% and lte 100% + assertEq(uint256(clip.tail()), values.collaterals[ilk].clip_tail, _concat("TestError/clip-tail-", ilk)); + if (ilk == "TUSD-A") { // long tail liquidation + assertTrue(clip.tail() >= 1200 && clip.tail() <= 30 days, _concat("TestError/TUSD-clip-tail-range-", ilk)); // gt eq 20 minutes and lt eq 30 days + } else { + assertTrue(clip.tail() >= 1200 && clip.tail() <= 12 hours, _concat("TestError/clip-tail-range-", ilk)); // gt eq 20 minutes and lt eq 12 hours + } + uint256 normalizedTestCusp = (values.collaterals[ilk].clip_cusp) * 10**23; + assertEq(uint256(clip.cusp()), normalizedTestCusp, _concat("TestError/clip-cusp-", ilk)); + assertTrue(clip.cusp() >= RAY / 10 && clip.cusp() < RAY, _concat("TestError/clip-cusp-range-", ilk)); // gte 10% and lt 100% + assertTrue(_rmul(clip.buf(), clip.cusp()) <= RAY, _concat("TestError/clip-buf-cusp-limit-", ilk)); + uint256 normalizedTestChip = (values.collaterals[ilk].clip_chip) * 10**14; + assertEq(uint256(clip.chip()), normalizedTestChip, _concat("TestError/clip-chip-", ilk)); + assertTrue(clip.chip() < 1 * WAD / 100, _concat("TestError/clip-chip-range-", ilk)); // lt 1% + uint256 normalizedTestTip = values.collaterals[ilk].clip_tip * RAD; + assertEq(uint256(clip.tip()), normalizedTestTip, _concat("TestError/clip-tip-", ilk)); + assertTrue(clip.tip() == 0 || clip.tip() >= RAD && clip.tip() <= 500 * RAD, _concat("TestError/clip-tip-range-", ilk)); + + assertEq(clip.wards(address(clipMom)), values.collaterals[ilk].clipper_mom, _concat("TestError/clip-clipperMom-auth-", ilk)); + + assertEq(clipMom.tolerance(address(clip)), values.collaterals[ilk].cm_tolerance * RAY / 10000, _concat("TestError/clipperMom-tolerance-", ilk)); + + if (values.collaterals[ilk].liqOn) { + assertEq(clip.stopped(), 0, _concat("TestError/clip-liqOn-", ilk)); + } else { + assertTrue(clip.stopped() > 0, _concat("TestError/clip-liqOn-", ilk)); + } + + assertEq(clip.wards(address(end)), 1, _concat("TestError/clip-end-auth-", ilk)); + assertEq(clip.wards(address(pauseProxy)), 1, _concat("TestError/clip-pause-proxy-auth-", ilk)); // Check pause_proxy ward + } + { + (bool exists, bytes memory value) = clip.calc().call(abi.encodeWithSignature("tau()")); + assertEq(exists ? abi.decode(value, (uint256)) : 0, values.collaterals[ilk].calc_tau, _concat("TestError/calc-tau-", ilk)); + (exists, value) = clip.calc().call(abi.encodeWithSignature("step()")); + assertEq(exists ? abi.decode(value, (uint256)) : 0, values.collaterals[ilk].calc_step, _concat("TestError/calc-step-", ilk)); + if (exists) { + assertTrue(abi.decode(value, (uint256)) > 0, _concat("TestError/calc-step-is-zero-", ilk)); + } + (exists, value) = clip.calc().call(abi.encodeWithSignature("cut()")); + uint256 normalizedTestCut = values.collaterals[ilk].calc_cut * 10**23; + assertEq(exists ? abi.decode(value, (uint256)) : 0, normalizedTestCut, _concat("TestError/calc-cut-", ilk)); + if (exists) { + assertTrue(abi.decode(value, (uint256)) > 0 && abi.decode(value, (uint256)) < RAY, _concat("TestError/calc-cut-range-", ilk)); + } + } + { + uint256 normalizedTestChop = (values.collaterals[ilk].chop * 10**14) + WAD; + uint256 _chost = (values.collaterals[ilk].dust * RAD) * normalizedTestChop / WAD; + assertEq(clip.chost(), _chost, _concat("TestError/calc-chost-incorrect-", ilk)); // Ensure clip.upchost() is called when dust changes + } + if (reg.class(ilk) == 7) { + // check correct clipper type is used for the reg.class 7 + address engine = LockstakeClipperLike(address(clip)).engine(); + assertNotEq(engine, address(0), _concat("TestError/clip-engine-is-not-set-", ilk)); + } + } + if (reg.class(ilk) < 3) { + { + GemJoinAbstract join = GemJoinAbstract(reg.join(ilk)); + assertEq(join.wards(address(pauseProxy)), 1, _concat("TestError/join-pause-proxy-auth-", ilk)); // Check pause_proxy ward + } + } + } + // Require that debt + (debt that could be drawn) does not exceed Line. + // TODO: consider a buffer for fee accrual + assertTrue(vat.debt() + sums[1] <= vat.Line(), "TestError/vat-Line-1"); + + // Enforce the global Line also falls between (sum of lines) + offset and (sum of lines) + 2*offset. + assertTrue(sums[0] + values.line_offset * RAD <= vat.Line(), "TestError/vat-Line-2"); + assertTrue(sums[0] + 2 * values.line_offset * RAD >= vat.Line(), "TestError/vat-Line-3"); + + // TODO: have a discussion about how we want to manage the global Line going forward. + } + + function _getOSMPrice(address pip) internal returns (uint256) { + // vm.load is to pull the price from the LP Oracle storage bypassing the whitelist + uint256 price = uint256(vm.load( + pip, + bytes32(uint256(3)) + )) & type(uint128).max; // Price is in the second half of the 32-byte storage slot + + // Price is bounded in the spot by around 10^23 + // Give a 10^9 buffer for price appreciation over time + // Note: This currently can't be hit due to the uint112, but we want to backstop + // once the PIP uint256 size is increased + assertTrue(price <= (10 ** 14) * WAD); + + return price; + } + + function _getUNIV2LPPrice(address pip) internal returns (uint256) { + // vm.load is to pull the price from the LP Oracle storage bypassing the whitelist + uint256 price = uint256(vm.load( + pip, + bytes32(uint256(3)) + )) & type(uint128).max; // Price is in the second half of the 32-byte storage slot + + // Price is bounded in the spot by around 10^23 + // Give a 10^9 buffer for price appreciation over time + // Note: This currently can't be hit due to the uint112, but we want to backstop + // once the PIP uint256 size is increased + assertTrue(price <= (10 ** 14) * WAD); + + return price; + } + + function _giveTokens(address token, uint256 amount) internal { + if (token == addr.addr("GUSD")) { + _giveTokensGUSD(token, amount); + return; + } + + GodMode.setBalance(token, address(this), amount); + } + + function _giveTokensGUSD(address _token, uint256 amount) internal { + DSTokenAbstract token = DSTokenAbstract(_token); + + if (token.balanceOf(address(this)) == amount) return; + + // Special exception GUSD has its storage in a separate contract + address STORE = 0xc42B14e49744538e3C239f8ae48A1Eaaf35e68a0; + + // Edge case - balance is already set for some reason + if (token.balanceOf(address(this)) == amount) return; + + for (uint256 i = 0; i < 200; i++) { + // Scan the storage for the balance storage slot + bytes32 prevValue = vm.load( + STORE, + keccak256(abi.encode(address(this), uint256(i))) + ); + vm.store( + STORE, + keccak256(abi.encode(address(this), uint256(i))), + bytes32(amount) + ); + if (token.balanceOf(address(this)) == amount) { + // Found it + return; + } else { + // Keep going after restoring the original value + vm.store( + STORE, + keccak256(abi.encode(address(this), uint256(i))), + prevValue + ); + } + } + + // We have failed if we reach here + assertTrue(false, "TestError/GiveTokens-slot-not-found"); + } + + function _checkIlkIntegration( + bytes32 _ilk, + GemJoinAbstract join, + ClipAbstract clip, + address pip, + bool _isOSM, + bool _checkLiquidations, + bool _transferFee + ) internal { + GemAbstract token = GemAbstract(join.gem()); + + if (_isOSM) OsmAbstract(pip).poke(); + vm.warp(block.timestamp + 3601); + if (_isOSM) OsmAbstract(pip).poke(); + spotter.poke(_ilk); + + // Authorization + assertEq(join.wards(pauseProxy), 1, _concat("TestError/checkIlkIntegration-pauseProxy-not-auth-on-join-", _ilk)); + assertEq(vat.wards(address(join)), 1, _concat("TestError/checkIlkIntegration-join-not-auth-on-vat-", _ilk)); + assertEq(vat.wards(address(clip)), 1, _concat("TestError/checkIlkIntegration-clip-not-auth-on-vat-", _ilk)); + assertEq(dog.wards(address(clip)), 1, _concat("TestError/checkIlkIntegration-clip-not-auth-on-dog-", _ilk)); + assertEq(clip.wards(address(dog)), 1, _concat("TestError/checkIlkIntegration-dog-not-auth-on-clip-", _ilk)); + assertEq(clip.wards(address(end)), 1, _concat("TestError/checkIlkIntegration-end-not-auth-on-clip-", _ilk)); + assertEq(clip.wards(address(clipMom)), 1, _concat("TestError/checkIlkIntegration-clipMom-not-auth-on-clip-", _ilk)); + assertEq(clip.wards(address(esm)), 1, _concat("TestError/checkIlkIntegration-esm-not-auth-on-clip-", _ilk)); + if (_isOSM) { + assertEq(OsmAbstract(pip).wards(address(osmMom)), 1, _concat("TestError/checkIlkIntegration-osmMom-not-auth-on-pip-", _ilk)); + assertEq(OsmAbstract(pip).bud(address(spotter)), 1, _concat("TestError/checkIlkIntegration-spot-not-bud-on-pip-", _ilk)); + assertEq(OsmAbstract(pip).bud(address(clip)), 1, _concat("TestError/checkIlkIntegration-spot-not-bud-on-pip-", _ilk)); + assertEq(OsmAbstract(pip).bud(address(clipMom)), 1, _concat("TestError/checkIlkIntegration-spot-not-bud-on-pip-", _ilk)); + assertEq(OsmAbstract(pip).bud(address(end)), 1, _concat("TestError/checkIlkIntegration-spot-not-bud-on-pip-", _ilk)); + assertEq(MedianAbstract(OsmAbstract(pip).src()).bud(pip), 1, _concat("TestError/checkIlkIntegration-pip-not-bud-on-osm-", _ilk)); + assertEq(OsmMomAbstract(osmMom).osms(_ilk), pip, _concat("TestError/checkIlkIntegration-pip-not-bud-on-osmMom-", _ilk)); + } + + (,,,, uint256 dust) = vat.ilks(_ilk); + dust /= RAY; + uint256 amount = 4 * dust * 10 ** uint256(token.decimals()) / (_isOSM ? _getOSMPrice(pip) : uint256(DSValueAbstract(pip).read())); + uint256 amount18 = token.decimals() == 18 ? amount : amount * 10**(18 - uint256(token.decimals())); + _giveTokens(address(token), amount); + + assertEq(token.balanceOf(address(this)), amount); + assertEq(vat.gem(_ilk, address(this)), 0); + token.approve(address(join), amount); + join.join(address(this), amount); + assertEq(token.balanceOf(address(this)), 0); + if (_transferFee) { + amount = vat.gem(_ilk, address(this)); + assertTrue(amount > 0); + } + assertEq(vat.gem(_ilk, address(this)), amount18); + + // Tick the fees forward so that art != dai in wad units + vm.warp(block.timestamp + 1); + jug.drip(_ilk); + + // Deposit collateral, generate DAI + (,uint256 rate,,uint256 line,) = vat.ilks(_ilk); + + assertEq(vat.dai(address(this)), 0); + // Set max line to ensure we can create a new position + _setIlkLine(_ilk, type(uint256).max); + vat.frob(_ilk, address(this), address(this), address(this), int256(amount18), int256(_divup(RAY * dust, rate))); + // Revert ilk line to proceed with testing + _setIlkLine(_ilk, line); + assertEq(vat.gem(_ilk, address(this)), 0); + assertTrue(vat.dai(address(this)) >= dust * RAY); + assertTrue(vat.dai(address(this)) <= (dust + 1) * RAY); + + // Payback DAI, withdraw collateral + vat.frob(_ilk, address(this), address(this), address(this), -int256(amount18), -int256(_divup(RAY * dust, rate))); + assertEq(vat.gem(_ilk, address(this)), amount18); + assertEq(vat.dai(address(this)), 0); + + // Withdraw from adapter + join.exit(address(this), amount); + if (_transferFee) { + amount = token.balanceOf(address(this)); + } + assertEq(token.balanceOf(address(this)), amount); + assertEq(vat.gem(_ilk, address(this)), 0); + + // Generate new DAI to force a liquidation + token.approve(address(join), amount); + join.join(address(this), amount); + if (_transferFee) { + amount = vat.gem(_ilk, address(this)); + } + // dart max amount of DAI + (,,uint256 spot,,) = vat.ilks(_ilk); + + // Set max line to ensure we can draw dai + _setIlkLine(_ilk, type(uint256).max); + vat.frob(_ilk, address(this), address(this), address(this), int256(amount18), int256(amount18 * spot / rate)); + // Revert ilk line to proceed with testing + _setIlkLine(_ilk, line); + + vm.warp(block.timestamp + 1); + jug.drip(_ilk); + assertEq(clip.kicks(), 0); + if (_checkLiquidations) { + if (_getIlkDuty(_ilk) == rates.rates(0)) { + // Rates wont accrue if 0, raise the mat to make the vault unsafe + _setIlkMat(_ilk, 100000 * RAY); + vm.warp(block.timestamp + 10 days); + spotter.poke(_ilk); + } + dog.bark(_ilk, address(this), address(this)); + assertEq(clip.kicks(), 1); + } + + // Dump all dai for next run + vat.move(address(this), address(0x0), vat.dai(address(this))); + } + + function _checkIlkClipper( + bytes32 ilk, + GemJoinAbstract join, + ClipAbstract clipper, + address calc, + OsmAbstract pip, + uint256 ilkAmt + ) internal { + + // Contracts set + assertEq(dog.vat(), address(vat)); + assertEq(dog.vow(), address(vow)); + { + (address clip,,,) = dog.ilks(ilk); + assertEq(clip, address(clipper)); + } + assertEq(clipper.ilk(), ilk); + assertEq(clipper.vat(), address(vat)); + assertEq(clipper.vow(), address(vow)); + assertEq(clipper.dog(), address(dog)); + assertEq(clipper.spotter(), address(spotter)); + assertEq(clipper.calc(), calc); + + // Authorization + assertEq(vat.wards(address(clipper)) , 1); + assertEq(dog.wards(address(clipper)) , 1); + assertEq(clipper.wards(address(dog)) , 1); + assertEq(clipper.wards(address(end)) , 1); + assertEq(clipper.wards(address(clipMom)), 1); + assertEq(clipper.wards(address(esm)), 1); + + try pip.bud(address(spotter)) returns (uint256 bud) { + assertEq(bud, 1); + } catch {} + try pip.bud(address(clipper)) returns (uint256 bud) { + assertEq(bud, 1); + } catch {} + try pip.bud(address(clipMom)) returns (uint256 bud) { + assertEq(bud, 1); + } catch {} + try pip.bud(address(end)) returns (uint256 bud) { + assertEq(bud, 1); + } catch {} + + // Force max Hole + vm.store( + address(dog), + bytes32(uint256(4)), + bytes32(type(uint256).max) + ); + + // Initially this test assume that's we are using freshly deployed Cliiper contract without any past auctions + if (clipper.kicks() > 0) { + // Cleanup clipper auction counter + vm.store( + address(clipper), + bytes32(uint256(10)), + bytes32(uint256(0)) + ); + + assertEq(clipper.kicks(), 0); + } + + // ----------------------- Check Clipper works and bids can be made ----------------------- + + { + GemAbstract token = GemAbstract(join.gem()); + uint256 tknAmt = ilkAmt / 10 ** (18 - join.dec()); + _giveTokens(address(token), tknAmt); + assertEq(token.balanceOf(address(this)), tknAmt); + + // Join to adapter + assertEq(vat.gem(ilk, address(this)), 0); + assertEq(token.allowance(address(this), address(join)), 0); + token.approve(address(join), tknAmt); + join.join(address(this), tknAmt); + assertEq(token.balanceOf(address(this)), 0); + assertEq(vat.gem(ilk, address(this)), ilkAmt); + } + + { + // Generate new DAI to force a liquidation + uint256 rate; + int256 art; + uint256 spot; + uint256 line; + (,rate, spot, line,) = vat.ilks(ilk); + art = int256(ilkAmt * spot / rate); + + // dart max amount of DAI + _setIlkLine(ilk, type(uint256).max); + vat.frob(ilk, address(this), address(this), address(this), int256(ilkAmt), art); + _setIlkLine(ilk, line); + _setIlkMat(ilk, 100000 * RAY); + vm.warp(block.timestamp + 10 days); + spotter.poke(ilk); + assertEq(clipper.kicks(), 0); + dog.bark(ilk, address(this), address(this)); + assertEq(clipper.kicks(), 1); + + (, rate,,,) = vat.ilks(ilk); + uint256 debt = rate * uint256(art) * dog.chop(ilk) / WAD; + vm.store( + address(vat), + keccak256(abi.encode(address(this), uint256(5))), + bytes32(debt) + ); + assertEq(vat.dai(address(this)), debt); + assertEq(vat.gem(ilk, address(this)), 0); + + vm.warp(block.timestamp + 20 minutes); + (, uint256 tab, uint256 lot, address usr,, uint256 top) = clipper.sales(1); + + assertEq(usr, address(this)); + assertEq(tab, debt); + assertEq(lot, ilkAmt); + assertTrue(lot * top > tab); // There is enough collateral to cover the debt at current price + + vat.hope(address(clipper)); + clipper.take(1, lot, top, address(this), bytes("")); + } + + { + (, uint256 tab, uint256 lot, address usr,,) = clipper.sales(1); + assertEq(usr, address(0)); + assertEq(tab, 0); + assertEq(lot, 0); + assertEq(vat.dai(address(this)), 0); + assertEq(vat.gem(ilk, address(this)), ilkAmt); // What was purchased + returned back as it is the owner of the vault + } + } + + struct LockstakeIlkParams { + bytes32 ilk; + uint256 fee; + address pip; + address lsmkr; + address engine; + address clip; + address calc; + address farm; + address rToken; + address rDistr; + uint256 rDur; + } + + function _checkLockstakeIlkIntegration( + LockstakeIlkParams memory p + ) internal { + LockstakeEngineLike engine = LockstakeEngineLike(p.engine); + StakingRewardsLike farm = StakingRewardsLike(p.farm); + // Check relevant contracts are correctly configured + { + assertEq(dog.vat(), address(vat), "checkLockstakeIlkIntegration/invalid-dog-vat"); + assertEq(dog.vow(), address(vow), "checkLockstakeIlkIntegration/invalid-dog-vow"); + (address clip,,,) = dog.ilks(p.ilk); + assertEq(clip, p.clip, "checkLockstakeIlkIntegration/invalid-dog-clip"); + assertEq(engine.voteDelegateFactory(), voteDelegateFactory, "checkLockstakeIlkIntegration/invalid-engine-voteDelegateFactory"); + assertEq(engine.usdsJoin(), address(usdsJoin), "checkLockstakeIlkIntegration/invalid-engine-usdsJoin"); + assertEq(engine.ilk(), p.ilk, "checkLockstakeIlkIntegration/invalid-engine-ilk"); + assertEq(engine.mkrSky(), address(mkrSky), "checkLockstakeIlkIntegration/invalid-engine-mkrSky"); + assertEq(engine.lsmkr(), p.lsmkr, "checkLockstakeIlkIntegration/invalid-engine-lsmkr"); + assertEq(engine.jug(), address(jug), "checkLockstakeIlkIntegration/invalid-engine-jug"); + assertEq(engine.fee(), p.fee * WAD / 100_00, "checkLockstakeIlkIntegration/invalid-fee"); + assertNotEq(p.farm, address(0), "checkLockstakeIlkIntegration/invalid-farm"); + assertEq(engine.farms(p.farm), 1, "checkLockstakeIlkIntegration/disabled-farm"); + assertEq(farm.stakingToken(), p.lsmkr, "checkLockstakeIlkIntegration/invalid-stakingToken"); + assertEq(farm.rewardsToken(), p.rToken, "checkLockstakeIlkIntegration/invalid-rewardsToken"); + assertEq(farm.rewardsDistribution(), p.rDistr, "checkLockstakeIlkIntegration/invalid-rewardsDistribution"); + assertEq(farm.rewardsDuration(), p.rDur, "checkLockstakeIlkIntegration/invalid-rewardsDuration"); + assertEq(ClipAbstract(p.clip).vat(), address(vat), "checkLockstakeIlkIntegration/invalid-clip-vat"); + assertEq(ClipAbstract(p.clip).spotter(), address(spotter), "checkLockstakeIlkIntegration/invalid-clip-spotter"); + assertEq(ClipAbstract(p.clip).dog(), address(dog), "checkLockstakeIlkIntegration/invalid-clip-dog"); + assertEq(ClipAbstract(p.clip).ilk(), p.ilk, "checkLockstakeIlkIntegration/invalid-clip-ilk"); + assertEq(ClipAbstract(p.clip).vow(), address(vow), "checkLockstakeIlkIntegration/invalid-clip-vow"); + assertEq(ClipAbstract(p.clip).calc(), p.calc, "checkLockstakeIlkIntegration/invalid-clip-calc"); + assertEq(LockstakeClipperLike(p.clip).engine(), p.engine, "checkLockstakeIlkIntegration/invalid-clip-engine"); + assertEq(LockstakeClipperLike(p.clip).stopped(), 0, "checkLockstakeIlkIntegration/invalid-clip-stopped"); + assertEq(osmMom.osms(p.ilk), p.pip, "checkLockstakeIlkIntegration/invalid-osmMom-pip"); + } + // Check ilk registry values + { + ( + string memory name, + string memory symbol, + uint256 _class, + uint256 decimals, + address gem, + address pip, + address gemJoin, + address clip + ) = reg.info(p.ilk); + assertEq(name, GemAbstract(p.lsmkr).name(), "checkLockstakeIlkIntegration/incorrect-reg-name"); + assertEq(symbol, GemAbstract(p.lsmkr).symbol(), "checkLockstakeIlkIntegration/incorrect-reg-symbol"); + assertEq(_class, 7, "checkLockstakeIlkIntegration/incorrect-reg-class"); // REG_CLASS_JOINLESS + assertEq(decimals, GemAbstract(p.lsmkr).decimals(), "checkLockstakeIlkIntegration/incorrect-reg-decimals"); + assertEq(gem, address(mkr), "checkLockstakeIlkIntegration/incorrect-reg-gem"); + assertEq(pip, p.pip, "checkLockstakeIlkIntegration/incorrect-reg-pip"); + assertEq(gemJoin, address(0), "checkLockstakeIlkIntegration/incorrect-reg-gemJoin"); + assertEq(clip, p.clip, "checkLockstakeIlkIntegration/incorrect-reg-clip"); + } + // Check required authorizations + { + assertEq(vat.wards(p.engine), 1, "checkLockstakeIlkIntegration/missing-auth-vat-engine"); + assertEq(vat.wards(p.clip), 1, "checkLockstakeIlkIntegration/missing-auth-vat-clip"); + assertEq(WardsAbstract(p.pip).wards(address(osmMom)), 1, "checkLockstakeIlkIntegration/missing-auth-pip-osmMom"); + assertEq(dog.wards(p.clip), 1, "checkLockstakeIlkIntegration/missing-auth-dog-clip"); + assertEq(WardsAbstract(p.lsmkr).wards(p.engine), 1, "checkLockstakeIlkIntegration/missing-auth-lsmkr-engine"); + assertEq(WardsAbstract(p.engine).wards(p.clip), 1, "checkLockstakeIlkIntegration/missing-auth-engine-clip"); + assertEq(WardsAbstract(p.clip).wards(address(dog)), 1, "checkLockstakeIlkIntegration/missing-auth-clip-dog"); + assertEq(WardsAbstract(p.clip).wards(address(end)), 1, "checkLockstakeIlkIntegration/missing-auth-clip-end"); + assertEq(WardsAbstract(p.clip).wards(address(clipMom)), 1, "checkLockstakeIlkIntegration/missing-auth-clip-clipMom"); + } + // Check required OSM buds + { + assertEq(OsmAbstract(p.pip).bud(address(spotter)), 1, "checkLockstakeIlkIntegration/missing-bud-spotter"); + assertEq(OsmAbstract(p.pip).bud(p.clip), 1, "checkLockstakeIlkIntegration/missing-bud-clip"); + assertEq(OsmAbstract(p.pip).bud(address(clipMom)), 1, "checkLockstakeIlkIntegration/missing-bud-clipMom"); + assertEq(OsmAbstract(p.pip).bud(address(end)), 1, "checkLockstakeIlkIntegration/missing-bud-end"); + } + // Prepare for liquidation + uint256 drawAmt; + uint256 lockAmt; + { + // Force max Hole + vm.store(address(dog), bytes32(uint256(4)), bytes32(type(uint256).max)); + // Reset auction count + if (ClipAbstract(p.clip).kicks() > 0) { + stdstore.target(p.clip).sig("kicks()").checked_write(uint256(0)); + assertEq(ClipAbstract(p.clip).kicks(), 0, "checkLockstakeIlkIntegration/unchanged-kicks"); + } + // Poke OSM price + OsmAbstract(p.pip).poke(); + vm.warp(block.timestamp + 1 hours); + OsmAbstract(p.pip).poke(); + spotter.poke(p.ilk); + // Calculate lock and draw amounts + (,,,, uint256 dust) = vat.ilks(p.ilk); + drawAmt = dust / RAY; + lockAmt = drawAmt * WAD / _getOSMPrice(p.pip) * 10; + // Give tokens + _giveTokens(address(mkr), lockAmt); + _giveTokens(address(sky), lockAmt * afterSpell.sky_mkr_rate); + } + uint256 snapshot = vm.snapshot(); + // Check locking and freeing Mkr + { + uint256 initialEngineBalance = mkr.balanceOf(p.engine); + engine.open(0); + assertEq(mkr.balanceOf(address(this)), lockAmt, "checkLockstakeIlkIntegration/LockAndFreeMkr/invalid-initial-balance"); + mkr.approve(address(engine), lockAmt); + engine.lock(address(this), 0, lockAmt, 0); + assertEq(mkr.balanceOf(p.engine), initialEngineBalance + lockAmt, "checkLockstakeIlkIntegration/LockAndFreeMkr/invalid-locked-mkr-balance"); + engine.free(address(this), 0, address(this), lockAmt); + uint256 exitFee = lockAmt * p.fee / 100_00; + assertEq(mkr.balanceOf(address(this)), lockAmt - exitFee, "checkLockstakeIlkIntegration/LockAndFreeMkr/invalid-unlocked-balance"); + vm.revertTo(snapshot); + } + // Check locking and freeing Sky + { + uint256 initialEngineBalance = mkr.balanceOf(p.engine); + engine.open(0); + uint256 skyAmt = lockAmt * afterSpell.sky_mkr_rate; + assertEq(sky.balanceOf(address(this)), skyAmt, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-initial-balance"); + sky.approve(address(engine), skyAmt); + engine.lockSky(address(this), 0, skyAmt, 0); + assertEq(mkr.balanceOf(p.engine), initialEngineBalance + lockAmt, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-locked-mkr-balance"); + engine.freeSky(address(this), 0, address(this), skyAmt); + uint256 exitFee = lockAmt * p.fee / 100_00 * afterSpell.sky_mkr_rate; + assertGe(sky.balanceOf(address(this)), skyAmt - exitFee, "checkLockstakeIlkIntegration/LockAndFreeSky/invalid-unlocked-balance"); + vm.revertTo(snapshot); + } + // Check drawing and wiping + { + uint256 initialEngineBalance = mkr.balanceOf(p.engine); + address urn = engine.open(0); + assertEq(mkr.balanceOf(address(this)), lockAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-initial-balance"); + mkr.approve(address(engine), lockAmt); + engine.lock(address(this), 0, lockAmt, 0); + assertEq(mkr.balanceOf(p.engine), initialEngineBalance + lockAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-locked-mkr-balance"); + engine.draw(address(this), 0, address(this), drawAmt); + assertEq(usds.balanceOf(address(this)), drawAmt, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-usds-balance-after-draw"); + skip(10 days); + jug.drip(p.ilk); + (, uint256 art) = vat.urns(p.ilk, urn); + (, uint256 rate,,,) = vat.ilks(p.ilk); + uint256 wipeAmt = _divup(art * rate, RAY); + assertGt(wipeAmt, drawAmt + 1 /* +1 to exclude rounding up */, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-wipe-after-draw"); + _giveTokens(address(usds), wipeAmt); + usds.approve(address(engine), wipeAmt); + engine.wipe(address(this), 0, wipeAmt); + assertEq(usds.balanceOf(address(this)), 0, "checkLockstakeIlkIntegration/DrawAndWipe/invalid-usds-balance-after-wipe"); + vm.revertTo(snapshot); + } + // Check farming and getting a reward + { + // Lock with selected farm + address urn = engine.open(0); + mkr.approve(address(engine), lockAmt); + engine.selectFarm(address(this), 0, p.farm, 0); + engine.lock(address(this), 0, lockAmt, 0); + assertEq(GemAbstract(p.farm).balanceOf(urn), lockAmt, "checkLockstakeIlkIntegration/FarmAndGetReward/FarmAndGetReward/invalid-urn-farm-balance"); + // Deposit rewards into farm and notify + uint256 rewardAmt = 1_000_000 * WAD; + address rewardsToken = farm.rewardsToken(); + deal(rewardsToken, p.farm, rewardAmt, true); + vm.prank(farm.rewardsDistribution()); farm.notifyRewardAmount(rewardAmt); + // Claim rewards + address rewardsUser = address(this); + skip(farm.rewardsDuration()); + uint256 resultAmt = engine.getReward(address(this), 0, p.farm, rewardsUser); + assertGt(resultAmt, 0, "checkLockstakeIlkIntegration/FarmAndGetReward/no-reward-amt"); + assertGt(GemAbstract(rewardsToken).balanceOf(rewardsUser), 0, "checkLockstakeIlkIntegration/FarmAndGetReward/no-reward-balance"); + vm.revertTo(snapshot); + } + // Check liquidations + _checkLockstakeTake(p, lockAmt, drawAmt, false, false); vm.revertTo(snapshot); + _checkLockstakeTake(p, lockAmt, drawAmt, false, true); vm.revertTo(snapshot); + _checkLockstakeTake(p, lockAmt, drawAmt, true, false); vm.revertTo(snapshot); + _checkLockstakeTake(p, lockAmt, drawAmt, true, true); vm.revertTo(snapshot); + } + + struct Sale { + uint256 pos; // Index in active array + uint256 tab; // Dai to raise [rad] + uint256 lot; // collateral to sell [wad] + uint256 tot; // static registry of total collateral to sell [wad] + address usr; // Liquidated CDP + uint96 tic; // Auction start time + uint256 top; // Starting price [ray] + } + + struct LockstakeBalances { + uint256 chiefMkr; + uint256 engineMkr; + uint256 farmLsmkr; + uint256 vatGem; + } + + function _checkLockstakeTake( + LockstakeIlkParams memory p, + uint256 lockAmt, + uint256 drawAmt, + bool withDelegate, + bool withStaking + ) internal { + // Open vault + LockstakeEngineLike engine = LockstakeEngineLike(p.engine); + vm.prank(address(123)); address voteDelegate = VoteDelegateFactoryLike(voteDelegateFactory).create(); + assertNotEq(voteDelegate, address(0), "checkLockstakeTake/invalid-voteDelegate-address"); + address urn = engine.open(0); + LockstakeBalances memory initialBalances = LockstakeBalances({ + chiefMkr: mkr.balanceOf(address(chief)), + engineMkr: mkr.balanceOf(p.engine), + farmLsmkr: GemAbstract(p.lsmkr).balanceOf(p.farm), + vatGem: vat.gem(p.ilk, p.clip) + }); + + // Lock and draw + if (withDelegate) { + engine.selectVoteDelegate(address(this), 0, voteDelegate); + } + if (withStaking) { + engine.selectFarm(address(this), 0, address(p.farm), 0); + } + mkr.approve(address(engine), lockAmt); + engine.lock(address(this), 0, lockAmt, 0); + engine.draw(address(this), 0, address(this), drawAmt); + if (withDelegate) { + assertEq(engine.urnVoteDelegates(urn), voteDelegate, "checkLockstakeTake/AfterLockDraw/withDelegate/invalid-voteDelegate-urn"); + assertEq(mkr.balanceOf(address(chief)) - initialBalances.chiefMkr, lockAmt, "checkLockstakeTake/AfterLockDraw/withDelegate/invalid-chief-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), initialBalances.engineMkr, "checkLockstakeTake/AfterLockDraw/withDelegate/invalid-engine-balance"); + } else { + assertEq(engine.urnVoteDelegates(urn), address(0), "checkLockstakeTake/AfterLockDraw/withoutDelegate/invalid-voteDelegate-urn"); + assertEq(mkr.balanceOf(address(chief)), initialBalances.chiefMkr, "checkLockstakeTake/AfterLockDraw/withoutDelegate/invalid-chief-mkr-balance"); + assertEq(mkr.balanceOf(p.engine), initialBalances.engineMkr + lockAmt, "checkLockstakeTake/AfterLockDraw/withoutDelegate/invalid-engine-balance"); + } + if (withStaking) { + assertEq(GemAbstract(p.lsmkr).balanceOf(urn), 0, "checkLockstakeTake/AfterLockDraw/withStaking/invalid-urn-lsgem-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), initialBalances.farmLsmkr + lockAmt, "checkLockstakeTake/AfterLockDraw/withStaking/invalid-farm-lsgem-balance"); + assertEq(GemAbstract(p.farm).balanceOf(urn), lockAmt, "checkLockstakeTake/AfterLockDraw/withStaking/invalid-urn-farm-balance"); + } else { + assertEq(GemAbstract(p.lsmkr).balanceOf(urn), lockAmt, "checkLockstakeTake/AfterLockDraw/withoutStaking/invalid-urn-lsgem-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), initialBalances.farmLsmkr, "checkLockstakeTake/AfterLockDraw/withoutStaking/invalid-farm-lsgem-balance"); + assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "checkLockstakeTake/AfterLockDraw/withoutStaking/invalid-urn-farm-balance"); + } + + // Force liquidation + if (withDelegate) { + vm.roll(block.number + 1); // Roll one block to allow freeing from chief + } + _setIlkMat(p.ilk, 100_000 * RAY); + spotter.poke(p.ilk); + assertEq(ClipAbstract(p.clip).kicks(), 0, "checkLockstakeTake/non-0-kicks"); + assertEq(engine.urnAuctions(urn), 0, "checkLockstakeTake/non-0-actions"); + uint256 id = dog.bark(p.ilk, urn, address(this)); + assertEq(ClipAbstract(p.clip).kicks(), 1, "checkLockstakeTake/AfterBark/no-kicks"); + assertEq(engine.urnAuctions(urn), 1, "checkLockstakeTake/AfterBark/no-actions"); + Sale memory sale; + (sale.pos, sale.tab, sale.lot, sale.tot, sale.usr, sale.tic, sale.top) = LockstakeClipperLike(p.clip).sales(id); + assertEq(sale.pos, 0, "checkLockstakeTake/AfterBark/invalid-sale.pos"); + assertGt(sale.tab, drawAmt * RAY, "checkLockstakeTake/AfterBark/invalid-sale.tab"); + assertEq(sale.lot, lockAmt, "checkLockstakeTake/AfterBark/invalid-sale.lot"); + assertEq(sale.tot, lockAmt, "checkLockstakeTake/AfterBark/invalid-sale.tot"); + assertEq(sale.usr, urn, "checkLockstakeTake/AfterBark/invalid-sale.usr"); + assertEq(sale.tic, block.timestamp, "checkLockstakeTake/AfterBark/invalid-sale.tic"); + assertEq(sale.top, _getOSMPrice(p.pip) * ClipAbstract(p.clip).buf() / WAD, "checkLockstakeTake/AfterBark/invalid-sale.top"); + assertEq(vat.gem(p.ilk, p.clip), initialBalances.vatGem + lockAmt, "checkLockstakeTake/AfterBark/invalid-vat-gem-clip"); + assertEq(mkr.balanceOf(p.engine), initialBalances.engineMkr + lockAmt, "checkLockstakeTake/AfterBark/invalid-engine-mkr-balance"); + assertEq(GemAbstract(p.lsmkr).balanceOf(urn), 0, "checkLockstakeTake/AfterBark/invalid-urn-lsgem-balance"); + if (withDelegate) { + assertEq(mkr.balanceOf(address(chief)), initialBalances.chiefMkr, "checkLockstakeTake/AfterBark/withDelegate/invalid-chief-mkr-balance"); + } + if (withStaking) { + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), initialBalances.farmLsmkr, "checkLockstakeTake/AfterBark/withStaking/invalid-farm-lsgem-balance"); + assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "checkLockstakeTake/AfterBark/withStaking/invalid-urn-farm-balance"); + } + + // Take auction + address buyer = address(888); + vm.prank(pauseProxy); vat.suck(address(0), buyer, sale.tab); + vm.prank(buyer); vat.hope(p.clip); + assertEq(mkr.balanceOf(buyer), 0, "checkLockstakeTake/AfterBark/invalid-buyer-mkr-balance"); + vm.prank(buyer); ClipAbstract(p.clip).take(id, lockAmt, type(uint256).max, buyer, ""); + assertGt(mkr.balanceOf(buyer), 0, "checkLockstakeTake/AfterTake/invalid-buyer-mkr-balance"); + (sale.pos, sale.tab, sale.lot, sale.tot, sale.usr, sale.tic, sale.top) = LockstakeClipperLike(p.clip).sales(id); + assertEq(sale.pos, 0, "checkLockstakeTake/AfterTake/invalid-sale.pos"); + assertEq(sale.tab, 0, "checkLockstakeTake/AfterTake/invalid-sale.tab"); + assertEq(sale.lot, 0, "checkLockstakeTake/AfterTake/invalid-sale.lot"); + assertEq(sale.tot, 0, "checkLockstakeTake/AfterTake/invalid-sale.tot"); + assertEq(sale.usr, address(0), "checkLockstakeTake/AfterTake/invalid-sale.usr"); + assertEq(sale.tic, 0, "checkLockstakeTake/AfterTake/invalid-sale.tic"); + assertEq(sale.top, 0, "checkLockstakeTake/AfterTake/invalid-sale.top"); + assertEq(vat.gem(p.ilk, p.clip), initialBalances.vatGem, "checkLockstakeTake/AfterTake/invalid-vat.gem"); + if (withDelegate) { + assertEq(mkr.balanceOf(address(chief)), initialBalances.chiefMkr, "checkLockstakeTake/AfterTake/withDelegate/invalid-chief-mkr-balance"); + } + if (withStaking) { + assertEq(GemAbstract(p.lsmkr).balanceOf(p.farm), initialBalances.farmLsmkr, "checkLockstakeTake/AfterTake/withStaking/invalid-lsgem-farm-balance"); + assertEq(GemAbstract(p.farm).balanceOf(urn), 0, "checkLockstakeTake/AfterTake/withStaking/invalid-farm-urn-balance"); + } + } + + function _checkUNILPIntegration( + bytes32 _ilk, + GemJoinAbstract join, + ClipAbstract clip, + LPOsmAbstract pip, + address _medianizer1, + address _medianizer2, + bool _isMedian1, + bool _isMedian2, + bool _checkLiquidations + ) internal { + GemAbstract token = GemAbstract(join.gem()); + + pip.poke(); + vm.warp(block.timestamp + 3601); + pip.poke(); + spotter.poke(_ilk); + + // Check medianizer sources + assertEq(pip.src(), address(token)); + assertEq(pip.orb0(), _medianizer1); + assertEq(pip.orb1(), _medianizer2); + + // Authorization + assertEq(join.wards(pauseProxy), 1); + assertEq(vat.wards(address(join)), 1); + assertEq(clip.wards(address(end)), 1); + assertEq(pip.wards(address(osmMom)), 1); + assertEq(pip.bud(address(spotter)), 1); + assertEq(pip.bud(address(end)), 1); + if (_isMedian1) assertEq(MedianAbstract(_medianizer1).bud(address(pip)), 1); + if (_isMedian2) assertEq(MedianAbstract(_medianizer2).bud(address(pip)), 1); + + (,,,, uint256 dust) = vat.ilks(_ilk); + dust /= RAY; + uint256 amount = 2 * dust * WAD / _getUNIV2LPPrice(address(pip)); + _giveTokens(address(token), amount); + + assertEq(token.balanceOf(address(this)), amount); + assertEq(vat.gem(_ilk, address(this)), 0); + token.approve(address(join), amount); + join.join(address(this), amount); + assertEq(token.balanceOf(address(this)), 0); + assertEq(vat.gem(_ilk, address(this)), amount); + + // Tick the fees forward so that art != dai in wad units + vm.warp(block.timestamp + 1); + jug.drip(_ilk); + + // Deposit collateral, generate DAI + (,uint256 rate,,,) = vat.ilks(_ilk); + assertEq(vat.dai(address(this)), 0); + vat.frob(_ilk, address(this), address(this), address(this), int256(amount), int256(_divup(RAY * dust, rate))); + assertEq(vat.gem(_ilk, address(this)), 0); + assertTrue(vat.dai(address(this)) >= dust * RAY && vat.dai(address(this)) <= (dust + 1) * RAY); + + // Payback DAI, withdraw collateral + vat.frob(_ilk, address(this), address(this), address(this), -int256(amount), -int256(_divup(RAY * dust, rate))); + assertEq(vat.gem(_ilk, address(this)), amount); + assertEq(vat.dai(address(this)), 0); + + // Withdraw from adapter + join.exit(address(this), amount); + assertEq(token.balanceOf(address(this)), amount); + assertEq(vat.gem(_ilk, address(this)), 0); + + // Generate new DAI to force a liquidation + token.approve(address(join), amount); + join.join(address(this), amount); + // dart max amount of DAI + (,,uint256 spot,,) = vat.ilks(_ilk); + vat.frob(_ilk, address(this), address(this), address(this), int256(amount), int256(amount * spot / rate)); + vm.warp(block.timestamp + 1); + jug.drip(_ilk); + assertEq(clip.kicks(), 0); + if (_checkLiquidations) { + dog.bark(_ilk, address(this), address(this)); + assertEq(clip.kicks(), 1); + } + + // Dump all dai for next run + vat.move(address(this), address(0x0), vat.dai(address(this))); + } + + function _checkPsmIlkIntegration( + bytes32 _ilk, + GemJoinAbstract join, + ClipAbstract clip, + address pip, + PsmAbstract psm, + uint256 tinBps, + uint256 toutBps + ) internal { + uint256 tin = tinBps * WAD / 100_00; + uint256 tout = toutBps * WAD / 100_00; + GemAbstract token = GemAbstract(join.gem()); + + // Check PIP is set (ilk exists) + assertTrue(pip != address(0)); + + // Update price (poke spotter) + spotter.poke(_ilk); + + // Authorization (check wards) + assertEq(join.wards(pauseProxy), 1); + assertEq(join.wards(address(psm)), 1); + assertEq(psm.wards(pauseProxy), 1); + assertEq(vat.wards(address(join)), 1); + assertEq(clip.wards(address(end)), 1); + + // Check tin / tout values of PSM + assertEq(psm.tin(), tin, _concat("Incorrect-tin-", _ilk)); + assertEq(psm.tout(), tout, _concat("Incorrect-tout-", _ilk)); + + // Arbitrary amount of TOKEN to test PSM sellGem and buyGem with (in whole units) + // `amount` is the amount of _TOKEN_ we are selling/buying (NOT measured in Dai) + uint256 amount = 100_000; + // Amount should be more than 10,000 as `tin` and `tout` are basis point measurements + require(amount >= 10_000, "checkPsmIlkIntegration/amount-too-low-for-precision-checks"); + + // Increase line where necessary to allow for coverage for both `buyGem` and `sellGem` + { + // Get the Art (current debt) and line (debt ceiling) for this PSM + (uint256 Art ,,, uint256 line,) = vat.ilks(_ilk); + // Normalize values to whole units so we can compare them + Art = Art / WAD; // `rate` is 1 * RAY for all PSMs + line = line / RAD; + + // If not enough room below line (e.g. Maxed out PSM) + if(Art + amount > line){ + _setIlkLine(_ilk, (Art + amount + 1) * RAD); // Increase `line` to `Art`+`amount` + } + } + + // Scale up `amount` to the correct Gem decimals value (buyGem and sellGem both use Gem decimals for precision) + amount = amount * WAD / _to18ConversionFactor(psm); + _giveTokens(address(token), amount); + + // Approvals + token.approve(address(join), amount); + dai.approve(address(psm), type(uint256).max); + + // Sell TOKEN _to_ the PSM for DAI (increases debt) + psm.sellGem(address(this), amount); + + amount = amount * (10 ** (18 - uint256(token.decimals()))); // Scale to Dai decimals (18) for Dai balance check + amount -= amount * tin / WAD; // Subtract `tin` fee (was deducted by PSM) + + assertEq(token.balanceOf(address(this)), 0, _concat("PSM.sellGem-token-balance-", _ilk)); + assertEq(dai.balanceOf(address(this)), amount, _concat("PSM.sellGem-dai-balance-", _ilk)); + + // For `sellGem` we had `amount` TOKENS, so there is no issue calling it + // For `buyGem` we have `amount` Dai, but `buyGem` takes `gemAmt` as TOKENS + // So we need to calculate the `gemAmt` of TOKEN we want to buy (i.e. subtract `tout` in advance) + amount -= _divup(amount * tout, WAD); // Subtract `tout` fee (i.e. convert to `gemAmt`) + amount = amount / (10 ** (18 - uint256(token.decimals()))); // Scale to Gem decimals for `buyGem()` + + // Buy TOKEN _from_ the PSM for DAI (decreases debt) + psm.buyGem(address(this), amount); + + // There may be some Dai dust left over depending on tout and decimals + // This should always be less than some dust limit + assertTrue(dai.balanceOf(address(this)) < 1 * WAD); // TODO lower this + assertEq(token.balanceOf(address(this)), amount, _concat("PSM.buyGem-token-balance-", _ilk)); + + // Dump all dai for next run + dai.transfer(address(0x0), dai.balanceOf(address(this))); + } + + struct LitePsmIlkIntegrationParams { + bytes32 ilk; + address pip; + address litePsm; + address pocket; + uint256 bufUnits; // `buf` as whole units + uint256 tinBps; // tin as bps + uint256 toutBps; // tout as bps + } + + function _checkLitePsmIlkIntegration(LitePsmIlkIntegrationParams memory p) internal { + uint256 tin = p.tinBps * WAD / 100_00; + uint256 tout = p.toutBps * WAD / 100_00; + LitePsmLike litePsm = LitePsmLike(p.litePsm); + GemAbstract token = GemAbstract(litePsm.gem()); + + // Authorization (check wards) + assertEq(litePsm.wards(address(pauseProxy)), 1, _concat("checkLitePsmIlkIntegration/pauseProxy-not-ward-", p.ilk)); + // pauseProxy can execute swaps with no fees + assertEq(litePsm.bud(address(pauseProxy)), 1, _concat("checkLitePsmIlkIntegration/pauseProxy-not-bud-", p.ilk)); + + // litePsm params are properly set + assertEq(litePsm.vow(), address(vow), _concat("checkLitePsmIlkIntegration/incorrect-vow-", p.ilk)); + assertEq(litePsm.daiJoin(), address(daiJoin), _concat("checkLitePsmIlkIntegration/incorrect-daiJoin-", p.ilk)); + assertEq(litePsm.pocket(), p.pocket, _concat("checkLitePsmIlkIntegration/incorrect-pocket-", p.ilk)); + assertEq(litePsm.buf(), p.bufUnits * WAD, _concat("checkLitePsmIlkIntegration/incorrect-buf-", p.ilk)); + assertEq(litePsm.tin(), tin, _concat("checkLitePsmIlkIntegration/incorrect-tin-", p.ilk)); + assertEq(litePsm.tout(), tout, _concat("checkLitePsmIlkIntegration/incorrect-tout-", p.ilk)); + + // Vat is properly initialized + { + // litePsm is given "unlimited" ink + (uint256 ink, ) = vat.urns(p.ilk, address(litePsm)); + assertEq(ink, type(uint256).max / RAY, _concat("checkLitePsmIlkIntegration/incorrect-vat-ink-", p.ilk)); + } + + // Spotter is properly initialized + { + (address pip,) = spotter.ilks(p.ilk); + assertEq(pip, p.pip, _concat("checkLitePsmIlkIntegration/incorrect-spot-pip-", p.ilk)); + } + + // Update price (poke spotter) + spotter.poke(p.ilk); + + // New PSM info is added to IlkRegistry + { + ( + string memory name, + string memory symbol, + uint256 _class, + uint256 decimals, + address gem, + address pip, + address gemJoin, + address clip + ) = reg.info(p.ilk); + + assertEq(name, token.name(), "checkLitePsmIlkIntegration/incorrect-reg-name"); + assertEq(symbol, token.symbol(), "checkLitePsmIlkIntegration/incorrect-reg-symbol"); + assertEq(_class, 6, "checkLitePsmIlkIntegration/incorrect-reg-class"); // REG_CLASS_JOINLESS + assertEq(decimals, token.decimals(), "checkLitePsmIlkIntegration/incorrect-reg-dec"); + assertEq(gem, address(token), "checkLitePsmIlkIntegration/incorrect-reg-gem"); + assertEq(pip, p.pip, "checkLitePsmIlkIntegration/incorrect-reg-pip"); + assertEq(gemJoin, address(0), "checkLitePsmIlkIntegration/incorrect-reg-gemJoin"); + assertEq(clip, address(0), "checkLitePsmIlkIntegration/incorrect-reg-xlip"); + } + + // ------ Test swap flows ------ + + // Arbitrary amount of TOKEN to test PSM sellGem and buyGem with (in whole units) + // `amount` is the amount of _TOKEN_ we are selling/buying (NOT measured in Dai) + uint256 amount = 100_000; + // Amount should be more than 10,000 as `tin` and `tout` are basis point measurements + require(amount >= 10_000, "checkLitePsmIlkIntegration/amount-too-low-for-precision-checks"); + + // Increase line where necessary to allow for coverage for both `buyGem` and `sellGem` + { + // Get the Art (current debt) and line (debt ceiling) for this PSM + (uint256 Art ,,, uint256 line,) = vat.ilks(p.ilk); + // Normalize values to whole units so we can compare them + Art = Art / WAD; // `rate` is 1 * RAY for all PSMs + line = line / RAD; + + // If not enough room below line (e.g. Maxed out PSM) + if (Art + amount > line) { + _setIlkLine(p.ilk, (Art + amount + 1) * RAD); // Increase `line` to `Art`+`amount` + } + + // If required, add pre-minted Dai to litePsm + if (litePsm.rush() > 0) { + litePsm.fill(); + } + } + + // Allow the test contract to sell or buy gems with no fees + GodMode.setWard(address(litePsm), address(this), 1); + litePsm.kiss(address(this)); + + // Approvals + token.approve(address(litePsm), type(uint256).max); + dai.approve(address(litePsm), type(uint256).max); + + // Scale up `amount` to the correct Gem decimals value (buyGem and sellGem both use Gem decimals for precision) + uint256 snapshot = vm.snapshot(); + + // Sell TOKEN _to_ the PSM for DAI (increases debt) + { + uint256 sellWadOut = amount * WAD; // Scale to Dai decimals (18) for Dai balance check + sellWadOut -= sellWadOut * tin / WAD; // Subtract `tin` fee (was deducted by PSM) + + uint256 sellAmt = amount * WAD / _to18ConversionFactor(litePsm); + _giveTokens(address(token), sellAmt); + litePsm.sellGem(address(this), sellAmt); + + assertEq(token.balanceOf(address(this)), 0, _concat("checkLitePsmIlkIntegration/sellGem-token-balance-", p.ilk)); + assertEq(dai.balanceOf(address(this)), sellWadOut, _concat("checkLitePsmIlkIntegration/sellGem-dai-balance-", p.ilk)); + + vm.revertTo(snapshot); + } + + // Sell TOKEN _to_ the PSM for DAI with no fees (increases debt) + { + litePsm.file("tin", 0.01 ether); // Force fee + uint256 sellWadOut = amount * WAD; // Scale to Dai decimals (18) for Dai balance check + + uint256 sellAmt = amount * WAD / _to18ConversionFactor(litePsm); + _giveTokens(address(token), sellAmt); + litePsm.sellGemNoFee(address(this), sellAmt); + + assertEq(token.balanceOf(address(this)), 0, _concat("checkLitePsmIlkIntegration/sellGemNoFee-token-balance-", p.ilk)); + assertEq(dai.balanceOf(address(this)), sellWadOut, _concat("checkLitePsmIlkIntegration/sellGemNoFee-dai-balance-", p.ilk)); + + vm.revertTo(snapshot); + } + + // For `sellGem` we had `amount` TOKENS, so there is no issue calling it + // For `buyGem` we have `amount` Dai, but `buyGem` takes `gemAmt` as TOKENS + // So we need to calculate the `gemAmt` of TOKEN we want to buy (i.e. subtract `tout` in advance) + + // Buy TOKEN _from_ the PSM for DAI (decreases debt) + { + uint256 buyWadIn = amount * WAD; // Scale to Dai decimals (18) for Dai balance check + buyWadIn += _divup(buyWadIn * tout, WAD); // Add `tout` fee + _giveTokens(address(dai), buyWadIn); // Mints Dai into the test contract + + uint256 buyAmt = amount * WAD / _to18ConversionFactor(litePsm); // Scale to Gem decimals for `buyGem()` + litePsm.buyGem(address(this), buyAmt); + + // There may be some Dai dust left over depending on tout and decimals + // This should always be less than some dust limit + assertLe(dai.balanceOf(address(this)), tout, _concat("checkLitePsmIlkIntegration/buyGem-dai-balance-", p.ilk)); + assertEq(token.balanceOf(address(this)), buyAmt, _concat("checkLitePsmIlkIntegration/buyGem-token-balance-", p.ilk)); + + vm.revertTo(snapshot); + } + + // Buy TOKEN _from_ the PSM for DAI with no fees (decreases debt) + { + litePsm.file("tout", 0.01 ether); // Force fee + + uint256 buyWadIn = amount * WAD; // Scale to Dai decimals (18) for Dai balance check + _giveTokens(address(dai), buyWadIn); // Mints Dai into the test contract + + uint256 buyAmt = amount * WAD / _to18ConversionFactor(litePsm); // Scale to Gem decimals for `buyGem()` + litePsm.buyGemNoFee(address(this), buyAmt); + + // There may be some Dai dust left over depending on tout and decimals + // This should always be less than some dust limit + assertLe(dai.balanceOf(address(this)), tout, _concat("checkLitePsmIlkIntegration/buyGemNoFee-dai-balance-", p.ilk)); + assertEq(token.balanceOf(address(this)), buyAmt, _concat("checkLitePsmIlkIntegration/buyGemNoFee-token-balance-", p.ilk)); + + vm.revertTo(snapshot); + } + + // ----- LitePsmMom can halt swaps ----- + + // LitePsmMom can halt litePSM + assertEq(litePsm.wards(address(litePsmMom)), 1, _concat("checkLitePsmIlkIntegration/litePsmMom-not-ward-", p.ilk)); + + // Gives the hat to the test contract, so it can invoke LitePsmMom + vm.store(address(chief), bytes32(uint256(0x0c)) /* `hat` slot */, bytes32(uint256(uint160(address(this))))); + LitePsmMomLike(address(litePsmMom)).halt(address(litePsm), 2 /* = BOTH */); + + assertEq(litePsm.tin(), type(uint256).max, _concat("checkLitePsmIlkIntegration/mom-halt-invalid-tin-", p.ilk)); + assertEq(litePsm.tout(), type(uint256).max, _concat("checkLitePsmIlkIntegration/mom-halt-invalid-tout-", p.ilk)); + } + + function _to18ConversionFactor(LitePsmLike litePsm) internal view returns (uint256) { + return litePsm.to18ConversionFactor(); + } + + function _to18ConversionFactor(PsmAbstract psm) internal view returns (uint256) { + return 10 ** (18 - GemJoinAbstract(psm.gemJoin()).dec()); + } + + function _checkDirectIlkIntegration( + bytes32 _ilk, + DirectDepositLike join, + ClipAbstract clip, + address pip, + uint256 bar, + uint256 tau + ) internal { + GemAbstract token = GemAbstract(join.gem()); + assertTrue(pip != address(0)); + + spotter.poke(_ilk); + + // Authorization + assertEq(join.wards(pauseProxy), 1); + assertEq(vat.wards(address(join)), 1); + assertEq(clip.wards(address(end)), 1); + assertEq(join.wards(address(esm)), 1); // Required in case of gov. attack + assertEq(join.wards(addr.addr("DIRECT_MOM")), 1); // Zero-delay shutdown for Aave gov. attack + + // Check the bar/tau/king are set correctly + assertEq(join.bar(), bar); + assertEq(join.tau(), tau); + assertEq(join.king(), pauseProxy); + + // Set the target bar to be super low to max out the debt ceiling + GodMode.setWard(address(join), address(this), 1); + join.file("bar", 1 * RAY / 10000); // 0.01% + join.deny(address(this)); + join.exec(); + + // Module should be maxed out + (,,, uint256 line,) = vat.ilks(_ilk); + (uint256 ink, uint256 art) = vat.urns(_ilk, address(join)); + assertEq(ink*RAY, line); + assertEq(art*RAY, line); + assertGe(token.balanceOf(address(join)), ink - 1); // Allow for small rounding error + + // Disable the module + GodMode.setWard(address(join), address(this), 1); + join.file("bar", 0); + join.deny(address(this)); + join.exec(); + + // Module should clear out + (ink, art) = vat.urns(_ilk, address(join)); + assertLe(ink, 1); + assertLe(art, 1); + assertEq(token.balanceOf(address(join)), 0); + + assertEq(join.tic(), 0); + } + + function _getSignatures(bytes32 signHash) internal returns (bytes memory signatures, address[] memory signers) { + // seeds chosen s.t. corresponding addresses are in ascending order + uint8[30] memory seeds = [8,10,6,2,9,15,14,20,7,29,24,13,12,25,16,26,21,22,0,18,17,27,3,28,23,19,4,5,1,11]; + uint256 numSigners = seeds.length; + signers = new address[](numSigners); + for(uint256 i; i < numSigners; i++) { + uint256 sk = uint256(keccak256(abi.encode(seeds[i]))); + signers[i] = vm.addr(sk); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(sk, signHash); + signatures = abi.encodePacked(signatures, r, s, v); + } + assertEq(signatures.length, numSigners * 65); + } + + function _oracleAuthRequestMint( + bytes32 sourceDomain, + bytes32 targetDomain, + uint256 toMint, + uint256 expectedFee + ) internal { + TeleportOracleAuthLike oracleAuth = TeleportOracleAuthLike(addr.addr("MCD_ORACLE_AUTH_TELEPORT_FW_A")); + GodMode.setWard(address(oracleAuth), address(this), 1); + (bytes memory signatures, address[] memory signers) = _getSignatures(oracleAuth.getSignHash(TeleportGUID({ + sourceDomain: sourceDomain, + targetDomain: targetDomain, + receiver: bytes32(uint256(uint160(address(this)))), + operator: bytes32(0), + amount: uint128(toMint), + nonce: 1, + timestamp: uint48(block.timestamp) + }))); + oracleAuth.addSigners(signers); + oracleAuth.requestMint(TeleportGUID({ + sourceDomain: sourceDomain, + targetDomain: targetDomain, + receiver: bytes32(uint256(uint160(address(this)))), + operator: bytes32(0), + amount: uint128(toMint), + nonce: 1, + timestamp: uint48(block.timestamp) + }), signatures, expectedFee, 0); + } + + function _checkTeleportFWIntegration( + bytes32 sourceDomain, + bytes32 targetDomain, + uint256 line, + address gateway, + address fee, + address escrow, + uint256 toMint, + uint256 expectedFee, + uint256 expectedTtl + ) internal { + TeleportJoinLike join = TeleportJoinLike(addr.addr("MCD_JOIN_TELEPORT_FW_A")); + TeleportRouterLike router = TeleportRouterLike(addr.addr("MCD_ROUTER_TELEPORT_FW_A")); + + // Sanity checks + assertEq(join.line(sourceDomain), line); + assertEq(join.fees(sourceDomain), address(fee)); + assertEq(dai.allowance(escrow, gateway), type(uint256).max); + assertEq(dai.allowance(gateway, address(router)), type(uint256).max); + assertEq(TeleportFeeLike(fee).fee(), expectedFee); + assertEq(TeleportFeeLike(fee).ttl(), expectedTtl); + assertEq(router.gateways(sourceDomain), gateway); + assertEq(router.domains(gateway), sourceDomain); + assertEq(TeleportBridgeLike(gateway).l1Escrow(), escrow); + assertEq(TeleportBridgeLike(gateway).l1TeleportRouter(), address(router)); + assertEq(TeleportBridgeLike(gateway).l1Token(), address(dai)); + + { + // NOTE: We are calling the router directly because the bridge code is minimal and unique to each domain + // This tests the slow path via the router + vm.startPrank(gateway); + router.requestMint(TeleportGUID({ + sourceDomain: sourceDomain, + targetDomain: targetDomain, + receiver: bytes32(uint256(uint160(address(this)))), + operator: bytes32(0), + amount: uint128(toMint), + nonce: 0, + timestamp: uint48(block.timestamp - TeleportFeeLike(fee).ttl()) + }), 0, 0); + vm.stopPrank(); + assertEq(dai.balanceOf(address(this)), toMint); + assertEq(join.debt(sourceDomain), int256(toMint)); + } + + // Check oracle auth mint -- add custom signatures to test + uint256 _fee = toMint * expectedFee / WAD; + { + uint256 prevDai = vat.dai(address(vow)); + _oracleAuthRequestMint(sourceDomain, targetDomain, toMint, expectedFee); + assertEq(dai.balanceOf(address(this)), toMint * 2 - _fee); + assertEq(join.debt(sourceDomain), int256(toMint * 2)); + assertEq(vat.dai(address(vow)) - prevDai, _fee * RAY); + } + + // Check settle + dai.transfer(gateway, toMint * 2 - _fee); + vm.startPrank(gateway); + router.settle(targetDomain, toMint * 2 - _fee); + vm.stopPrank(); + assertEq(dai.balanceOf(gateway), 0); + assertEq(join.debt(sourceDomain), int256(_fee)); + } + + function _checkCureLoadTeleport( + bytes32 sourceDomain, + bytes32 targetDomain, + uint256 toMint, + uint256 expectedFee, + uint256 expectedTell, + bool cage + ) internal { + TeleportJoinLike join = TeleportJoinLike(addr.addr("MCD_JOIN_TELEPORT_FW_A")); + + // Oracle auth mint -- add custom signatures to test + _oracleAuthRequestMint(sourceDomain, targetDomain, toMint, expectedFee); + assertEq(join.debt(sourceDomain), int256(toMint)); + + // Emulate Global Settlement + if (cage) { + assertEq(cure.live(), 1); + vm.store( + address(cure), + keccak256(abi.encode(address(this), uint256(0))), + bytes32(uint256(1)) + ); + cure.cage(); + assertEq(cure.tell(), 0); + } + assertEq(cure.live(), 0); + + // Check cure tells the teleport source correctly + cure.load(address(join)); + assertEq(cure.tell(), expectedTell); + } + + struct VestStream { + uint256 id; + address usr; + uint256 bgn; + uint256 clf; + uint256 fin; + uint256 tau; + address mgr; + uint256 res; + uint256 tot; + uint256 rxd; + } + + function _checkVestDai(VestStream[] memory _ss) internal { + uint256 prevStreamCount = vestDai.ids(); + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + // Check that all streams added in this spell are tested + assertEq(vestDai.ids(), prevStreamCount + _ss.length, "testVestDai/not-all-streams-tested"); + + for (uint256 i = 0; i < _ss.length; i++) { + _checkVestStream("testVestDai", vestDai, address(dai), _ss[i]); + } + } + + function _checkVestMkr(VestStream[] memory _ss) internal { + uint256 prevStreamCount = vestMkr.ids(); + uint256 prevAllowance = gov.allowance(pauseProxy, address(vestMkr)); + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + // Check allowance was increased according to the streams + uint256 sumTot = 0; + uint256 sumRxd = 0; + for (uint256 i = 0; i < _ss.length; i++) { + sumTot = sumTot + _ss[i].tot; + sumRxd = sumRxd + _ss[i].rxd; + } + assertEq(gov.allowance(pauseProxy, address(vestMkr)), prevAllowance + sumTot - sumRxd, "testVestMkr/invalid-allowance"); + + // Check that all streams added in this spell are tested + assertEq(vestMkr.ids(), prevStreamCount + _ss.length, "testVestMrk/not-all-streams-tested"); + + for (uint256 i = 0; i < _ss.length; i++) { + _checkVestStream("testVestMkr", vestMkr, address(gov), _ss[i]); + } + } + + function _checkTransferrableVestMkrAllowance() internal { + uint256 vestableAmt; + + for(uint256 i = 1; i <= vestMkr.ids(); i++) { + if (vestMkr.valid(i)) { + (,,,,,,uint128 tot, uint128 rxd) = vestMkr.awards(i); + vestableAmt = vestableAmt + (tot - rxd); + } + } + + uint256 allowance = gov.allowance(pauseProxy, address(vestMkr)); + assertGe(allowance, vestableAmt, "TestError/insufficient-gov-transferrable-vest-mkr-allowance"); + } + + + function _checkVestSky(VestStream[] memory _ss) internal { + uint256 prevStreamCount = vestSky.ids(); + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + // Check that all streams added in this spell are tested + assertEq(vestSky.ids(), prevStreamCount + _ss.length, "testVestSky/not-all-streams-tested"); + + for (uint256 i = 0; i < _ss.length; i++) { + _checkVestStream("testVestSky", vestSky, address(sky), _ss[i]); + } + } + + function _checkVestStream( + string memory _errPrefix, + VestAbstract _vest, + address _token, + VestStream memory _s + ) internal { + assertEq(_vest.usr(_s.id), _s.usr, _concat(_errPrefix, string("/usr"))); + assertEq(_vest.bgn(_s.id), _s.bgn, _concat(_errPrefix, string("/bgn"))); + assertEq(_vest.clf(_s.id), _s.clf, _concat(_errPrefix, string("/clf"))); + assertEq(_vest.fin(_s.id), _s.fin, _concat(_errPrefix, string("/fin"))); + assertEq(_vest.fin(_s.id), _s.bgn + _s.tau, _concat(_errPrefix, string("/fin (bgn + tau)"))); + assertEq(_vest.mgr(_s.id), _s.mgr, _concat(_errPrefix, string("/mgr"))); + assertEq(_vest.res(_s.id), _s.res, _concat(_errPrefix, string("/res"))); + assertEq(_vest.tot(_s.id), _s.tot, _concat(_errPrefix, string("/tot"))); + assertEq(_vest.rxd(_s.id), _s.rxd, _concat(_errPrefix, string("/rxd"))); + + GemAbstract token = GemAbstract(_token); + + { + uint256 before = vm.snapshot(); + + // Check each new stream is payable in the future + uint256 pbalance = token.balanceOf(_s.usr); + GodMode.setWard(address(_vest), address(this), 1); + _vest.unrestrict(_s.id); + + vm.warp(_s.fin); + _vest.vest(_s.id); + assertEq( + token.balanceOf(_s.usr), + pbalance + _s.tot - _s.rxd, + _concat(_errPrefix, string("/invalid-received-amount")) + ); + + vm.revertTo(before); + } + } + + function _getIlkMat(bytes32 _ilk) internal view returns (uint256 mat) { + (, mat) = spotter.ilks(_ilk); + } + + function _getIlkDuty(bytes32 _ilk) internal view returns (uint256 duty) { + (duty,) = jug.ilks(_ilk); + } + + function _setIlkMat(bytes32 ilk, uint256 amount) internal { + vm.store( + address(spotter), + bytes32(uint256(keccak256(abi.encode(ilk, uint256(1)))) + 1), + bytes32(amount) + ); + assertEq(_getIlkMat(ilk), amount, _concat("TestError/setIlkMat-", ilk)); + } + + function _setIlkRate(bytes32 ilk, uint256 amount) internal { + vm.store( + address(vat), + bytes32(uint256(keccak256(abi.encode(ilk, uint256(2)))) + 1), + bytes32(amount) + ); + (,uint256 rate,,,) = vat.ilks(ilk); + assertEq(rate, amount, _concat("TestError/setIlkRate-", ilk)); + } + + function _setIlkLine(bytes32 ilk, uint256 amount) internal { + vm.store( + address(vat), + bytes32(uint256(keccak256(abi.encode(ilk, uint256(2)))) + 3), + bytes32(amount) + ); + (,,,uint256 line,) = vat.ilks(ilk); + assertEq(line, amount, _concat("TestError/setIlkLine-", ilk)); + } + + function _checkIlkLerpOffboarding(bytes32 _ilk, bytes32 _lerp, uint256 _startMat, uint256 _endMat) internal { + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + LerpAbstract lerp = LerpAbstract(lerpFactory.lerps(_lerp)); + + vm.warp(block.timestamp + lerp.duration() / 2); + assertEq(_getIlkMat(_ilk), _startMat * RAY / 100); + lerp.tick(); + _assertEqApprox(_getIlkMat(_ilk), ((_startMat + _endMat) / 2) * RAY / 100, RAY / 100); + + vm.warp(block.timestamp + lerp.duration()); + lerp.tick(); + assertEq(_getIlkMat(_ilk), _endMat * RAY / 100); + } + + function _checkIlkLerpIncreaseMatOffboarding(bytes32 _ilk, bytes32 _oldLerp, bytes32 _newLerp, uint256 _newEndMat) internal { + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + LerpFactoryAbstract OLD_LERP_FAB = LerpFactoryAbstract(0x00B416da876fe42dd02813da435Cc030F0d72434); + LerpAbstract oldLerp = LerpAbstract(OLD_LERP_FAB.lerps(_oldLerp)); + + uint256 t = (block.timestamp - oldLerp.startTime()) * WAD / oldLerp.duration(); + uint256 tickMat = oldLerp.end() * t / WAD + oldLerp.start() - oldLerp.start() * t / WAD; + assertEq(_getIlkMat(_ilk), tickMat); + assertEq(spotter.wards(address(oldLerp)), 0); + + LerpAbstract newLerp = LerpAbstract(lerpFactory.lerps(_newLerp)); + + vm.warp(block.timestamp + newLerp.duration() / 2); + assertEq(_getIlkMat(_ilk), tickMat); + newLerp.tick(); + _assertEqApprox(_getIlkMat(_ilk), (tickMat + _newEndMat * RAY / 100) / 2, RAY / 100); + + vm.warp(block.timestamp + newLerp.duration()); + newLerp.tick(); + assertEq(_getIlkMat(_ilk), _newEndMat * RAY / 100); + } + + function _getExtcodesize(address target) internal view returns (uint256 exsize) { + assembly { + exsize := extcodesize(target) + } + } + + function _getBytecodeMetadataLength(address a) internal view returns (uint256 length) { + // The Solidity compiler encodes the metadata length in the last two bytes of the contract bytecode. + assembly { + let ptr := mload(0x40) + let size := extcodesize(a) + if iszero(lt(size, 2)) { + extcodecopy(a, ptr, sub(size, 2), 2) + length := mload(ptr) + length := shr(240, length) + length := add(length, 2) // the two bytes used to specify the length are not counted in the length + } + // We'll return zero if the bytecode is shorter than two bytes. + } + } + + /** + * @dev Checks if the deployer of a contract has not kept `wards` access to the contract. + * Notice that it depends on `deployers` being kept up-to-date. + */ + function _checkWards(address _addr, string memory contractName) internal { + for (uint256 i = 0; i < deployers.count(); i ++) { + address deployer = deployers.addr(i); + (bool ok, bytes memory data) = _addr.call(abi.encodeWithSignature("wards(address)", deployer)); + if (!ok || data.length != 32) return; + + uint256 ward = abi.decode(data, (uint256)); + if (ward > 0) { + emit log_named_address(" Deployer Address", deployer); + emit log_named_string(" Affected Contract", contractName); + fail("Error: Bad Auth"); + } + } + } + + /** + * @dev Same as `_checkWards`, but for OSMs' underlying Median contracts. + */ + function _checkOsmSrcWards(address _addr, string memory contractName) internal { + (bool ok, bytes memory data) = _addr.call(abi.encodeWithSignature("src()")); + if (!ok || data.length != 32) return; + + address source = abi.decode(data, (address)); + string memory sourceName = _concat("src of ", contractName); + _checkWards(source, sourceName); + } + + /** + * @notice Checks if the the deployer of a contract the chainlog has not kept `wards` access to it. + * @dev Reverts if `key` is not in the chainlog. + */ + function _checkAuth(bytes32 key) internal { + address _addr = chainLog.getAddress(key); + string memory contractName = _bytes32ToString(key); + + _checkWards(_addr, contractName); + _checkOsmSrcWards(_addr, contractName); + } + + function _checkRWADocUpdate(bytes32 ilk, string memory currentDoc, string memory newDoc) internal { + (string memory doc, address pip, uint48 tau, uint48 toc) = liquidationOracle.ilks(ilk); + + assertEq(doc, currentDoc, _concat("TestError/bad-old-document-for-", ilk)); + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + (string memory docNew, address pipNew, uint48 tauNew, uint48 tocNew) = liquidationOracle.ilks(ilk); + + assertEq(docNew, newDoc, _concat("TestError/bad-new-document-for-", ilk)); + assertEq(pip, pipNew, _concat("TestError/pip-is-not-the-same-for-", ilk)); + assertTrue(tau == tauNew, _concat("TestError/tau-is-not-the-same-for-", ilk)); + assertTrue(toc == tocNew, _concat("TestError/toc-is-not-the-same-for", ilk)); + } + + function _testGeneral() internal { + string memory description = new DssSpell().description(); + assertTrue(bytes(description).length > 0, "TestError/spell-description-length"); + // DS-Test can't handle strings directly, so cast to a bytes32. + assertEq(_stringToBytes32(spell.description()), + _stringToBytes32(description), "TestError/spell-description"); + + if(address(spell) != address(spellValues.deployed_spell)) { + assertEq(spell.expiration(), block.timestamp + spellValues.expiration_threshold, "TestError/spell-expiration"); + } else { + assertEq(spell.expiration(), spellValues.deployed_spell_created + spellValues.expiration_threshold, "TestError/spell-expiration"); + } + + assertTrue(spell.officeHours() == spellValues.office_hours_enabled, "TestError/spell-office-hours"); + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + _checkSystemValues(afterSpell); + + _checkCollateralValues(afterSpell); + } + + function _testOfficeHours() internal { + assertEq(spell.officeHours(), spellValues.office_hours_enabled, "TestError/office-hours-mismatch"); + + // Only relevant if office hours are enabled + if (spell.officeHours()) { + + _vote(address(spell)); + spell.schedule(); + + uint256 afterSchedule = vm.snapshot(); + + // Cast in the wrong day + { + uint256 castTime = block.timestamp + pause.delay(); + uint256 day = (castTime / 1 days + 3) % 7; + if (day < 5) { + castTime += 5 days - day * 86400; + } + + // Original revert reason is swallowed and "ds-pause-delegatecall-error" reason is given, + // so it's not worth bothering to check the revert reason. + vm.expectRevert(); + vm.warp(castTime); + spell.cast(); + } + + vm.revertTo(afterSchedule); + + // Cast too early in the day + + { + uint256 castTime = block.timestamp + pause.delay() + 24 hours; + uint256 hour = castTime / 1 hours % 24; + if (hour >= 14) { + castTime -= hour * 3600 - 13 hours; + } + + vm.expectRevert(); + vm.warp(castTime); + spell.cast(); + } + + vm.revertTo(afterSchedule); + + // Cast too late in the day + + { + uint256 castTime = block.timestamp + pause.delay(); + uint256 hour = castTime / 1 hours % 24; + if (hour < 21) { + castTime += 21 hours - hour * 3600; + } + + vm.expectRevert(); + vm.warp(castTime); + spell.cast(); + } + + } + } + + function _testCastOnTime() internal { + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + } + + function _testCastCost() internal { + _vote(address(spell)); + spell.schedule(); + + _castPreviousSpell(); + vm.warp(spell.nextCastTime()); + uint256 startGas = gasleft(); + spell.cast(); + uint256 endGas = gasleft(); + uint256 totalGas = startGas - endGas; + + assertTrue(spell.done(), "TestError/spell-not-done"); + // Fail if cast is too expensive + assertLe(totalGas, 15 * MILLION, "TestError/spell-cast-cost-too-high"); + } + + function _testDeployCost() internal { + uint256 startGas = gasleft(); + new DssSpell(); + uint256 endGas = gasleft(); + uint256 totalGas = startGas - endGas; + + // Warn if deploy exceeds block target size + if (totalGas > 15 * MILLION) { + emit log("Warn: deploy gas exceeds average block target"); + emit log_named_uint(" deploy gas", totalGas); + emit log_named_uint(" block target", 15 * MILLION); + } + + // Fail if deploy is too expensive + assertLe(totalGas, 30 * MILLION, "TestError/spell-deploy-cost-too-high"); + } + + // Fail when contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). + // This contract may not be deployable. + // Consider enabling the optimizer (with a low "runs" value!), + // turning off revert strings, or using libraries. + function _testContractSize() internal { + uint256 _sizeSpell; + address _spellAddr = address(spell); + assembly { + _sizeSpell := extcodesize(_spellAddr) + } + assertLe(_sizeSpell, 24576, "testContractSize/DssSpell-exceeds-max-contract-size"); + + uint256 _sizeAction; + address _actionAddr = spell.action(); + assembly { + _sizeAction := extcodesize(_actionAddr) + } + assertLe(_sizeAction, 24576, "testContractSize/DssSpellAction-exceeds-max-contract-size"); + + } + + // The specific date doesn't matter that much since function is checking for difference between warps + function _testNextCastTime() internal { + vm.warp(1606161600); // Nov 23, 20 UTC (could be cast Nov 26) + + _vote(address(spell)); + spell.schedule(); + + uint256 monday_1400_UTC = 1606744800; // Nov 30, 2020 + uint256 monday_2100_UTC = 1606770000; // Nov 30, 2020 + + // Day tests + vm.warp(monday_1400_UTC); // Monday, 14:00 UTC + assertEq(spell.nextCastTime(), monday_1400_UTC); // Monday, 14:00 UTC + + if (spell.officeHours()) { + vm.warp(monday_1400_UTC - 1 days); // Sunday, 14:00 UTC + assertEq(spell.nextCastTime(), monday_1400_UTC); // Monday, 14:00 UTC + + vm.warp(monday_1400_UTC - 2 days); // Saturday, 14:00 UTC + assertEq(spell.nextCastTime(), monday_1400_UTC); // Monday, 14:00 UTC + + vm.warp(monday_1400_UTC - 3 days); // Friday, 14:00 UTC + assertEq(spell.nextCastTime(), monday_1400_UTC - 3 days); // Able to cast + + vm.warp(monday_2100_UTC); // Monday, 21:00 UTC + assertEq(spell.nextCastTime(), monday_1400_UTC + 1 days); // Tuesday, 14:00 UTC + + vm.warp(monday_2100_UTC - 1 days); // Sunday, 21:00 UTC + assertEq(spell.nextCastTime(), monday_1400_UTC); // Monday, 14:00 UTC + + vm.warp(monday_2100_UTC - 2 days); // Saturday, 21:00 UTC + assertEq(spell.nextCastTime(), monday_1400_UTC); // Monday, 14:00 UTC + + vm.warp(monday_2100_UTC - 3 days); // Friday, 21:00 UTC + assertEq(spell.nextCastTime(), monday_1400_UTC); // Monday, 14:00 UTC + + // Time tests + uint256 castTime; + + for(uint256 i = 0; i < 5; i++) { + castTime = monday_1400_UTC + i * 1 days; // Next day at 14:00 UTC + vm.warp(castTime - 1 seconds); // 13:59:59 UTC + assertEq(spell.nextCastTime(), castTime); + + vm.warp(castTime + 7 hours + 1 seconds); // 21:00:01 UTC + if (i < 4) { + assertEq(spell.nextCastTime(), monday_1400_UTC + (i + 1) * 1 days); // Next day at 14:00 UTC + } else { + assertEq(spell.nextCastTime(), monday_1400_UTC + 7 days); // Next monday at 14:00 UTC (friday case) + } + } + } + } + + function _testRevertIfNotScheduled() internal { + vm.expectRevert(); + spell.nextCastTime(); + } + + function _testUseEta() internal { + vm.warp(1606161600); // Nov 23, 20 UTC (could be cast Nov 26) + + _vote(address(spell)); + spell.schedule(); + + uint256 castTime = spell.nextCastTime(); + assertGe(castTime, spell.eta()); + } + + // Verifies that the bytecode of the action of the spell used for testing + // matches what we'd expect. + // + // Not a complete replacement for Etherscan verification, unfortunately. + // This is because the DssSpell bytecode is non-deterministic because it + // deploys the action in its constructor and incorporates the action + // address as an immutable variable--but the action address depends on the + // address of the DssSpell which depends on the address+nonce of the + // deploying address. If we had a way to simulate a contract creation by + // an arbitrary address+nonce, we could verify the bytecode of the DssSpell + // instead. + // + // Vacuous until the deployed_spell value is non-zero. + function _testBytecodeMatches() internal { + // The DssSpell bytecode is non-deterministic, compare only code size + DssSpell expectedSpell = new DssSpell(); + assertEq(_getExtcodesize(address(spell)), _getExtcodesize(address(expectedSpell)), "TestError/spell-codesize"); + + // The SpellAction bytecode can be compared after chopping off the metada + address expectedAction = expectedSpell.action(); + address actualAction = spell.action(); + uint256 expectedBytecodeSize; + uint256 actualBytecodeSize; + assembly { + expectedBytecodeSize := extcodesize(expectedAction) + actualBytecodeSize := extcodesize(actualAction) + } + + uint256 metadataLength = _getBytecodeMetadataLength(expectedAction); + assertTrue(metadataLength <= expectedBytecodeSize, "TestError/metadata-length-gt-expected-bytecode-size"); + expectedBytecodeSize -= metadataLength; + + metadataLength = _getBytecodeMetadataLength(actualAction); + assertTrue(metadataLength <= actualBytecodeSize, "TestError/metadata-length-gt-actual-bytecode-size"); + actualBytecodeSize -= metadataLength; + + assertEq(actualBytecodeSize, expectedBytecodeSize, "TestError/bytecode-size-mismatch"); + uint256 size = actualBytecodeSize; + uint256 expectedHash; + uint256 actualHash; + assembly { + let ptr := mload(0x40) + + extcodecopy(expectedAction, ptr, 0, size) + expectedHash := keccak256(ptr, size) + + extcodecopy(actualAction, ptr, 0, size) + actualHash := keccak256(ptr, size) + } + assertEq(actualHash, expectedHash, "TestError/bytecode-hash-mismatch"); + } + + struct ChainlogCache { + bytes32 versionHash; + bytes32 contentHash; + uint256 count; + bytes32[] keys; + address[] values; + } + + /** + * @dev Checks the integrity of the chainlog. + * This test case is able to catch the following spell issues: + + * 1. Modifications without version bumping: + * a. Removing a key. + * b. Updating a key. + * c. Adding a key. + * d. Removing a key and adding it back (this can change the order of the list). + * 2. Version bumping without modifications. + * 3. Dangling wards on new or updated keys. + * + * When adding or updating a key, the test will automatically check for dangling wards if applicable. + * Notice that when a key is removed, if it is not the last one, there is a side-effect of moving + * the last key to the position of the removed one (well-known Solidity iterability pattern). + * This will generate a false-positive that will cause the test to re-check wards for the moved key. + */ + function _testChainlogIntegrity() internal { + ChainlogCache memory cacheBefore = ChainlogCache({ + count: chainLog.count(), + keys: chainLog.list(), + versionHash: keccak256(abi.encodePacked("")), + contentHash: keccak256(abi.encode(new bytes32[](0), new address[](0))), + values: new address[](0) + }); + + cacheBefore.values = new address[](cacheBefore.count); + for(uint256 i = 0; i < cacheBefore.count; i++) { + cacheBefore.values[i] = chainLog.getAddress(cacheBefore.keys[i]); + } + + cacheBefore.versionHash = keccak256(abi.encodePacked(chainLog.version())); + // Using `abi.encode` to prevent ambiguous encoding + cacheBefore.contentHash = keccak256(abi.encode(cacheBefore.keys, cacheBefore.values)); + + ////////////////////////////////////////// + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + ////////////////////////////////////////// + + ChainlogCache memory cacheAfter = ChainlogCache({ + count: chainLog.count(), + keys: chainLog.list(), + versionHash: keccak256(abi.encodePacked("")), + contentHash: keccak256(abi.encode(new bytes32[](0), new address[](0))), + values: new address[](0) + }); + + cacheAfter.values = new address[](cacheAfter.count); + for(uint256 i = 0; i < cacheAfter.count; i++) { + cacheAfter.values[i] = chainLog.getAddress(cacheAfter.keys[i]); + } + + cacheAfter.versionHash = keccak256(abi.encodePacked(chainLog.version())); + // Using `abi.encode` to prevent ambiguous encoding + cacheAfter.contentHash = keccak256(abi.encode(cacheAfter.keys, cacheAfter.values)); + + ////////////////////////////////////////// + + // If neither the version or the content have changed, there is nothing to test + if (cacheAfter.versionHash == cacheBefore.versionHash && cacheAfter.contentHash == cacheBefore.contentHash) { + vm.skip(true); + } + + // If the version is the same, the content should not have changed + if (cacheAfter.versionHash == cacheBefore.versionHash) { + assertEq(cacheBefore.count, cacheAfter.count, "TestError/chainlog-version-not-updated-length-change"); + + // Add explicit check otherwise this would fail with an array-out-of-bounds error, + // since Foundry does not halt the execution when an assertion fails. + if (cacheBefore.count == cacheAfter.count) { + // Fail if the chainlog is the same size, but EITHER: + // 1. The value for a specific key changed + // 2. The order of keys changed + for (uint256 i = 0; i < cacheAfter.count; i++) { + assertEq( + cacheBefore.values[i], + cacheAfter.values[i], + _concat( + "TestError/chainlog-version-not-updated-value-change: ", + _concat( + _concat("+ ", cacheAfter.keys[i]), + _concat(" | - ", cacheBefore.keys[i]) + ) + ) + ); + } + } + } else { + // If the version changed, the content should have changed + assertTrue(cacheAfter.contentHash != cacheBefore.contentHash, "TestError/chainlog-version-updated-no-content-change"); + } + + // If the content has changed, we look into the diff + if (cacheAfter.contentHash != cacheBefore.contentHash) { + // If the content changed, the version should have changed + assertTrue(cacheAfter.versionHash != cacheBefore.versionHash, "TestError/chainlog-content-updated-no-version-change"); + + uint256 diffCount; + // Iteration must stop at the shorter array length + uint256 maxIters = cacheAfter.count > cacheBefore.count ? cacheBefore.count : cacheAfter.count; + + // Look for changes in existing keys + for (uint256 i = 0; i < maxIters; i++) { + if (cacheAfter.keys[i] != cacheBefore.keys[i]) { + // Change in order + diffCount += 1; + } else if (cacheAfter.values[i] != cacheBefore.values[i]) { + // Change in value + diffCount += 1; + } + } + + // Account for new keys + // Notice: we don't care about removed keys + if (cacheAfter.count > cacheBefore.count) { + diffCount += (cacheAfter.count - cacheBefore.count); + } + + //////////////////////////////////////// + + bytes32[] memory diffKeys = new bytes32[](diffCount); + uint256 j = 0; + + for (uint256 i = 0; i < maxIters; i++) { + if (cacheAfter.keys[i] != cacheBefore.keys[i]) { + // Mark keys whose order has changed + diffKeys[j++] = cacheAfter.keys[i]; + } else if (cacheAfter.values[i] != cacheBefore.values[i]) { + // Mark changed values + diffKeys[j++] = cacheAfter.keys[i]; + } + } + + // Mark new keys + if (cacheAfter.count > cacheBefore.count) { + for (uint256 i = cacheBefore.count; i < cacheAfter.count; i++) { + diffKeys[j++] = cacheAfter.keys[i]; + } + } + + for (uint256 i = 0; i < diffKeys.length; i++) { + _checkAuth(diffKeys[i]); + } + } + } + + // Validate addresses in test harness match chainlog + function _testChainlogValues() internal { + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + bytes32[] memory keys = chainLog.list(); + for (uint256 i = 0; i < keys.length; i++) { + assertEq( + chainLog.getAddress(keys[i]), + addr.addr(keys[i]), + _concat("TestError/chainlog-vs-harness-key-mismatch: ", keys[i]) + ); + } + + assertEq(chainLog.version(), afterSpell.chainlog_version, "TestError/chainlog-version-mismatch"); + } + + function _checkCropCRVLPIntegration( + bytes32 _ilk, + CropJoinLike join, + ClipAbstract clip, + CurveLPOsmLike pip, + address _medianizer1, + address _medianizer2, + bool _isMedian1, + bool _isMedian2, + bool _checkLiquidations + ) public { + pip.poke(); + vm.warp(block.timestamp + 3601); + pip.poke(); + spotter.poke(_ilk); + + // Check medianizer sources + assertEq(pip.orbs(0), _medianizer1); + assertEq(pip.orbs(1), _medianizer2); + + // Contracts set + { + (address _clip,,,) = dog.ilks(_ilk); + assertEq(_clip, address(clip)); + } + assertEq(clip.ilk(), _ilk); + assertEq(clip.vat(), address(vat)); + assertEq(clip.vow(), address(vow)); + assertEq(clip.dog(), address(dog)); + assertEq(clip.spotter(), address(spotter)); + + // Authorization + assertEq(join.wards(pauseProxy), 1); + assertEq(vat.wards(address(join)), 1); + assertEq(vat.wards(address(clip)), 1); + assertEq(dog.wards(address(clip)), 1); + assertEq(clip.wards(address(dog)), 1); + assertEq(clip.wards(address(end)), 1); + assertEq(clip.wards(address(clipMom)), 1); + assertEq(clip.wards(address(esm)), 1); + assertEq(pip.wards(address(osmMom)), 1); + assertEq(pip.bud(address(spotter)), 1); + assertEq(pip.bud(address(end)), 1); + assertEq(pip.bud(address(clip)), 1); + assertEq(pip.bud(address(clipMom)), 1); + if (_isMedian1) assertEq(MedianAbstract(_medianizer1).bud(address(pip)), 1); + if (_isMedian2) assertEq(MedianAbstract(_medianizer2).bud(address(pip)), 1); + + (,,,, uint256 dust) = vat.ilks(_ilk); + uint256 amount = 2 * dust / (_getUNIV2LPPrice(address(pip)) * 1e9); + _giveTokens(address(join.gem()), amount); + + assertEq(GemAbstract(join.gem()).balanceOf(address(this)), amount); + assertEq(vat.gem(_ilk, cropper.getOrCreateProxy(address(this))), 0); + GemAbstract(join.gem()).approve(address(cropper), amount); + cropper.join(address(join), address(this), amount); + assertEq(GemAbstract(join.gem()).balanceOf(address(this)), 0); + assertEq(vat.gem(_ilk, cropper.getOrCreateProxy(address(this))), amount); + + // Tick the fees forward so that art != dai in wad units + vm.warp(block.timestamp + 1); + jug.drip(_ilk); + + // Check that we got rewards from the time increment above + assertEq(GemAbstract(join.bonus()).balanceOf(address(this)), 0); + cropper.join(address(join), address(this), 0); + // NOTE: LDO rewards are shutting off on Friday so this will fail (bad timing), but they plan to extend + //assertGt(GemAbstract(join.bonus()).balanceOf(address(this)), 0); + + // Deposit collateral, generate DAI + (,uint256 rate,,,) = vat.ilks(_ilk); + assertEq(vat.dai(address(this)), 0); + cropper.frob(_ilk, address(this), address(this), address(this), int256(amount), int256(_divup(dust, rate))); + assertEq(vat.gem(_ilk, cropper.getOrCreateProxy(address(this))), 0); + assertTrue(vat.dai(address(this)) >= dust && vat.dai(address(this)) <= dust + RAY); + + // Payback DAI, withdraw collateral + vat.hope(address(cropper)); // Need to grant the cropper permission to remove dai + cropper.frob(_ilk, address(this), address(this), address(this), -int256(amount), -int256(_divup(dust, rate))); + assertEq(vat.gem(_ilk, cropper.getOrCreateProxy(address(this))), amount); + assertEq(vat.dai(address(this)), 0); + + // Withdraw from adapter + cropper.exit(address(join), address(this), amount); + assertEq(GemAbstract(join.gem()).balanceOf(address(this)), amount); + assertEq(vat.gem(_ilk, cropper.getOrCreateProxy(address(this))), 0); + + if (_checkLiquidations) { + // Generate new DAI to force a liquidation + GemAbstract(join.gem()).approve(address(cropper), amount); + cropper.join(address(join), address(this), amount); + // dart max amount of DAI + { // Stack too deep + (,,uint256 spot,,) = vat.ilks(_ilk); + cropper.frob(_ilk, address(this), address(this), address(this), int256(amount), int256(amount * spot / rate)); + } + vm.warp(block.timestamp + 1); + jug.drip(_ilk); + assertEq(clip.kicks(), 0); + + // Kick off the liquidation + dog.bark(_ilk, cropper.getOrCreateProxy(address(this)), address(this)); + assertEq(clip.kicks(), 1); + + // Complete the liquidation + vat.hope(address(clip)); + (, uint256 tab,,,,) = clip.sales(1); + vm.store( + address(vat), + keccak256(abi.encode(address(this), uint256(5))), + bytes32(tab) + ); + assertEq(vat.dai(address(this)), tab); + assertEq(vat.gem(_ilk, cropper.getOrCreateProxy(address(this))), 0); + clip.take(1, type(uint256).max, type(uint256).max, address(this), ""); + assertEq(vat.gem(_ilk, cropper.getOrCreateProxy(address(this))), amount); + } + + // Dump all dai for next run + vat.move(address(this), address(0x0), vat.dai(address(this))); + } + + function _testSplitter() internal { + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done()); + + assertEq(vow.flapper(), address(split), "TestError/invalid-vow-flapper"); + assertEq(split.flapper(), address(flap), "TestError/invalid-split-flapper"); + assertEq(flap.gem(), address(sky), "TestError/invalid-flapper-gem"); + assertEq(flap.pip(), addr.addr("FLAP_SKY_ORACLE"), "TestError/invalid-flapper-pip"); + assertEq(flap.pair(), addr.addr("UNIV2USDSSKY"), "TestError/invalid-flapper-pair"); + + // Check splitter and flapper + { + // Leave surplus buffer ready to be flapped + vow.heal(vat.sin(address(vow)) - (vow.Sin() + vow.Ash())); + // Ensure flapping is possible + stdstore + .target(address(vat)) + .sig("dai(address)") + .with_key(address(vow)) + .checked_write(vat.sin(address(vow)) + vow.bump() + vow.hump()); + + GemAbstract pair = GemAbstract(addr.addr("UNIV2USDSSKY")); + FlapOracleLike pip = FlapOracleLike(flap.pip()); + + vm.prank(address(flap)); + uint256 price = uint256(pip.read()); + + // Ensure there is enough liquidity + uint256 usdsWad = 150_000_000 * WAD; + GodMode.setBalance(address(usds), address(pair), usdsWad); + // Ensure price is within the tolerane (flap.want() + delta (1 p.p.)) + uint256 skyWad = usdsWad * (flap.want() + 10**16) / price; + GodMode.setBalance(address(sky), address(pair), skyWad); + + uint256 lotRad = vow.bump() * split.burn() / WAD; + uint256 payWad = (vow.bump() - lotRad) / RAY; + + uint256 pskyBalancePauseProxy = sky.balanceOf(pauseProxy); + uint256 pdaiVow = vat.dai(address(vow)); + uint256 preserveUsds = usds.balanceOf(address(pair)); + uint256 preserveSky = sky.balanceOf(address(pair)); + + uint256 pbalanceUsdsFarm; + // Checking the farm balance is only relevant if split.burn() < 100% + if (split.burn() < 1 * WAD) { + pbalanceUsdsFarm = usds.balanceOf(split.farm()); + assertFalse(split.farm() == address(0), "TestError/Splitter/missing-farm"); + } + + vow.flap(); + + assertGt(sky.balanceOf(pauseProxy), pskyBalancePauseProxy, "TestError/Flapper/unexpected-sky-pause-proxy-balance"); + assertLt(sky.balanceOf(address(pair)), preserveSky, "TestError/Flapper/unexpected-sky-pair-balance"); + assertEq(usds.balanceOf(address(pair)), preserveUsds + lotRad / RAY, "TestError/Flapper/invalid-usds-pair-balance-increase"); + assertEq(pdaiVow - vat.dai(address(vow)), vow.bump(), "TestError/Flapper/invalid-vat-dai-vow-change"); + assertEq(usds.balanceOf(address(flap)), 0, "TestError/Flapper/invalid-usds-balance"); + assertEq(sky.balanceOf(address(flap)), 0, "TestError/Flapper/invalid-sky-balance"); + + if (split.burn() < 1 * WAD) { + assertEq(usds.balanceOf(split.farm()), pbalanceUsdsFarm + payWad, "TestError/Splitter/invalid-farm-balance"); + } + } + + // Check Mom can increase hop + { + // The check for the configured value is already done in `_checkSystemValues()` + assertLt(split.hop(), type(uint256).max, "TestError/SplitterMom/already-stopped"); + vm.prank(chief.hat()); + splitterMom.stop(); + assertEq(split.hop(), type(uint256).max, "TestError/SplitterMom/not-stopped"); + } + } + + function _testSystemTokens() internal { + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done()); + + // USDS + { + // USDS is upgradeable, so we need to ensure the implementation contract address is correct. + assertEq(_imp(address(usds)), addr.addr("USDS_IMP"), "TestError/USDS/invalid-usds-implementation"); + } + + // Converter: Dai <-> USDS + { + DaiUsdsLike daiUsds = DaiUsdsLike(addr.addr("DAI_USDS")); + address daiHolder = address(0x42); + deal(address(dai), daiHolder, 1_000 * WAD); + address usdsHolder = address(0x65); + deal(address(usds), usdsHolder, 1_000 * WAD); + + + // Dai -> USDS conversion + { + uint256 before = vm.snapshot(); + + uint256 pdaiBalance = dai.balanceOf(daiHolder); + uint256 pusdsBalance = usds.balanceOf(usdsHolder); + + vm.startPrank(daiHolder); + dai.approve(address(daiUsds), type(uint256).max); + daiUsds.daiToUsds(usdsHolder, pdaiBalance); + vm.stopPrank(); + + uint256 expectedUsdsBalance = pusdsBalance + pdaiBalance; + + assertEq(dai.balanceOf(daiHolder), 0, "TestError/Dai/bad-dai-to-usds-conversion"); + assertEq(usds.balanceOf(usdsHolder), expectedUsdsBalance, "TestError/Usds/bad-dai-to-usds-conversion"); + + vm.revertTo(before); + } + + // USDS -> Dai conversion + { + uint256 before = vm.snapshot(); + + uint256 pusdsBalance = usds.balanceOf(usdsHolder); + uint256 pdaiBalance = dai.balanceOf(daiHolder); + + vm.startPrank(usdsHolder); + usds.approve(address(daiUsds), type(uint256).max); + daiUsds.usdsToDai(daiHolder, pusdsBalance); + vm.stopPrank(); + + uint256 expectedDaiBalance = pdaiBalance + pusdsBalance; + + assertEq(usds.balanceOf(usdsHolder), 0, "TestError/USDS/bad-usds-to-dai-conversion"); + assertEq(dai.balanceOf(daiHolder), expectedDaiBalance, "TestError/Dai/bad-usds-to-dai-conversion"); + + vm.revertTo(before); + } + } + + // Converter: MKR <-> SKY + { + address mkrHolder = address(0x42); + deal(address(gov), mkrHolder, 1_000 * WAD); + address skyHolder = address(0x65); + deal(address(sky), skyHolder, 1_000 * WAD * afterSpell.sky_mkr_rate); + + + // MKR -> SKY conversion + { + uint256 before = vm.snapshot(); + + uint256 pmkrBalance = gov.balanceOf(mkrHolder); + uint256 pskyBalance = sky.balanceOf(skyHolder); + + vm.startPrank(mkrHolder); + gov.approve(address(mkrSky), type(uint256).max); + mkrSky.mkrToSky(skyHolder, pmkrBalance); + vm.stopPrank(); + + uint256 expectedSkyBalance = pskyBalance + (pmkrBalance * afterSpell.sky_mkr_rate); + + assertEq(gov.balanceOf(mkrHolder), 0, "TestError/MKR/bad-mkr-to-sky-conversion"); + assertEq(sky.balanceOf(skyHolder), expectedSkyBalance, "TestError/Sky/bad-mkr-to-sky-conversion"); + + vm.revertTo(before); + } + + // SKY -> MKR conversion + { + uint256 before = vm.snapshot(); + + uint256 pskyBalance = sky.balanceOf(skyHolder); + uint256 pmkrBalance = gov.balanceOf(mkrHolder); + + vm.startPrank(skyHolder); + sky.approve(address(mkrSky), type(uint256).max); + mkrSky.skyToMkr(mkrHolder, pskyBalance); + vm.stopPrank(); + + uint256 expectedMkrBalance = pmkrBalance + (pskyBalance / afterSpell.sky_mkr_rate); + + assertEq(sky.balanceOf(skyHolder), 0, "TestError/SKY/bad-sky-to-mkr-conversion"); + assertEq(gov.balanceOf(mkrHolder), expectedMkrBalance, "TestError/Mkr/bad-sky-to-mkr-conversion"); + + vm.revertTo(before); + } + } + + // sUSDS + { + // sUSDS is upgradeable, so we need to ensure the implementation contract address is correct. + assertEq(_imp(address(susds)), addr.addr("SUSDS_IMP"), "TestError/sUSDS/invalid-susds-implementation"); + assertEq(susds.asset(), address(usds), "TestError/sUSDS/invalid-susds-asset"); + + // Ensure rate accumulator is up-to-date + susds.drip(); + // Ensure the test contract has some tokens + _giveTokens(address(usds), 1_000 * WAD); + usds.approve(address(susds), type(uint256).max); + + uint256 pchi = susds.chi(); + uint256 passets = usds.balanceOf(address(this)); + + uint256 shares = susds.deposit(passets, address(this)); + assertLe(shares, passets, "TestError/sUSDS/invalid-shares"); + + uint256 interval = 365 days; + skip(interval); + susds.drip(); + + uint256 chi = susds.chi(); + uint256 expectedChi = _rpow(rates.rates(afterSpell.susds_ssr), interval, RAY) * pchi / RAY; + uint256 assets = susds.redeem(shares, address(this), address(this)); + + // Allow a 0.01% rounding error + assertApproxEqRel(chi, expectedChi, 10**14, "TestError/sUSDS/invalid-chi"); + assertGt(assets, passets, "TestError/sUSDS/invalid-redeem-assets"); + assertEq(assets, usds.balanceOf(address(this)), "TestError/sUSDS/invalid-balance-after-redeem"); + } + } + + // Obtained as `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)` + bytes32 constant EIP1967_IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + /// @dev Returns the implementation of upgradeable contracts following EIP1697 + function _imp(address _tgt) internal view returns (address) { + return address(uint160(uint256(vm.load(_tgt, EIP1967_IMPLEMENTATION_SLOT)))); + } +} diff --git a/archive/2024-10-17-DssSpell/DssSpell.t.sol b/archive/2024-10-17-DssSpell/DssSpell.t.sol new file mode 100644 index 000000000..5c668e7e8 --- /dev/null +++ b/archive/2024-10-17-DssSpell/DssSpell.t.sol @@ -0,0 +1,1021 @@ +// SPDX-FileCopyrightText: © 2020 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity 0.8.16; + +import "./DssSpell.t.base.sol"; +import {ScriptTools} from "dss-test/DssTest.sol"; + +import {RootDomain} from "dss-test/domains/RootDomain.sol"; +import {OptimismDomain} from "dss-test/domains/OptimismDomain.sol"; +import {ArbitrumDomain} from "dss-test/domains/ArbitrumDomain.sol"; + +interface L2Spell { + function dstDomain() external returns (bytes32); + function gateway() external returns (address); +} + +interface L2Gateway { + function validDomains(bytes32) external returns (uint256); +} + +interface BridgeLike { + function l2TeleportGateway() external view returns (address); +} + +interface ProxyLike { + function exec(address target, bytes calldata args) external payable returns (bytes memory out); +} + +interface SpellActionLike { + function dao_resolutions() external view returns (string memory); +} + +interface SequencerLike { + function getMaster() external view returns (bytes32); + function hasJob(address job) external view returns (bool); +} + +interface RwaLiquidationOracleLike { + function good(bytes32 ilk) external view returns (bool); + function ilks(bytes32) external view returns (string memory doc, address pip, uint48 tau, uint48 toc); +} + +contract DssSpellTest is DssSpellTestBase { + string config; + RootDomain rootDomain; + OptimismDomain optimismDomain; + ArbitrumDomain arbitrumDomain; + + // DO NOT TOUCH THE FOLLOWING TESTS, THEY SHOULD BE RUN ON EVERY SPELL + function testGeneral() public { + _testGeneral(); + } + + function testOfficeHours() public { + _testOfficeHours(); + } + + function testCastOnTime() public { + _testCastOnTime(); + } + + function testNextCastTime() public { + _testNextCastTime(); + } + + function testRevertIfNotScheduled() public { + _testRevertIfNotScheduled(); + } + + function testUseEta() public { + _testUseEta(); + } + + function testContractSize() public skippedWhenDeployed { + _testContractSize(); + } + + function testDeployCost() public skippedWhenDeployed { + _testDeployCost(); + } + + function testBytecodeMatches() public skippedWhenNotDeployed { + _testBytecodeMatches(); + } + + function testCastCost() public { + _testCastCost(); + } + + function testChainlogIntegrity() public { + _testChainlogIntegrity(); + } + + function testChainlogValues() public { + _testChainlogValues(); + } + + function testSplitter() public { + _testSplitter(); + } + + function testSystemTokens() public { + _testSystemTokens(); + } + + // Leave this test always enabled as it acts as a config test + function testPSMs() public { + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + bytes32 _ilk; + + // USDC + _ilk = "PSM-USDC-A"; + assertEq(addr.addr("MCD_JOIN_PSM_USDC_A"), reg.join(_ilk)); + assertEq(addr.addr("MCD_CLIP_PSM_USDC_A"), reg.xlip(_ilk)); + assertEq(addr.addr("PIP_USDC"), reg.pip(_ilk)); + assertEq(addr.addr("MCD_PSM_USDC_A"), chainLog.getAddress("MCD_PSM_USDC_A")); + _checkPsmIlkIntegration( + _ilk, + GemJoinAbstract(addr.addr("MCD_JOIN_PSM_USDC_A")), + ClipAbstract(addr.addr("MCD_CLIP_PSM_USDC_A")), + addr.addr("PIP_USDC"), + PsmAbstract(addr.addr("MCD_PSM_USDC_A")), + 0, // tin + 0 // tout + ); + + // GUSD + _ilk = "PSM-GUSD-A"; + assertEq(addr.addr("MCD_JOIN_PSM_GUSD_A"), reg.join(_ilk)); + assertEq(addr.addr("MCD_CLIP_PSM_GUSD_A"), reg.xlip(_ilk)); + assertEq(addr.addr("PIP_GUSD"), reg.pip(_ilk)); + assertEq(addr.addr("MCD_PSM_GUSD_A"), chainLog.getAddress("MCD_PSM_GUSD_A")); + _checkPsmIlkIntegration( + _ilk, + GemJoinAbstract(addr.addr("MCD_JOIN_PSM_GUSD_A")), + ClipAbstract(addr.addr("MCD_CLIP_PSM_GUSD_A")), + addr.addr("PIP_GUSD"), + PsmAbstract(addr.addr("MCD_PSM_GUSD_A")), + 0, // tin + 0 // tout + ); + + // USDP + _ilk = "PSM-PAX-A"; + assertEq(addr.addr("MCD_JOIN_PSM_PAX_A"), reg.join(_ilk)); + assertEq(addr.addr("MCD_CLIP_PSM_PAX_A"), reg.xlip(_ilk)); + assertEq(addr.addr("PIP_PAX"), reg.pip(_ilk)); + assertEq(addr.addr("MCD_PSM_PAX_A"), chainLog.getAddress("MCD_PSM_PAX_A")); + _checkPsmIlkIntegration( + _ilk, + GemJoinAbstract(addr.addr("MCD_JOIN_PSM_PAX_A")), + ClipAbstract(addr.addr("MCD_CLIP_PSM_PAX_A")), + addr.addr("PIP_PAX"), + PsmAbstract(addr.addr("MCD_PSM_PAX_A")), + 0, // tin + 0 // tout + ); + } + + // Leave this test always enabled as it acts as a config test + function testLitePSMs() public { + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + bytes32 _ilk; + + // USDC + _ilk = "LITE-PSM-USDC-A"; + assertEq(addr.addr("PIP_USDC"), reg.pip(_ilk)); + assertEq(addr.addr("MCD_LITE_PSM_USDC_A"), chainLog.getAddress("MCD_LITE_PSM_USDC_A")); + _checkLitePsmIlkIntegration( + LitePsmIlkIntegrationParams({ + ilk: _ilk, + pip: addr.addr("PIP_USDC"), + litePsm: addr.addr("MCD_LITE_PSM_USDC_A"), + pocket: addr.addr("MCD_LITE_PSM_USDC_A_POCKET"), + bufUnits: 400_000_000, + tinBps: 0, + toutBps: 0 + }) + ); + } + + // END OF TESTS THAT SHOULD BE RUN ON EVERY SPELL + + // TESTS BELOW CAN BE ENABLED/DISABLED ON DEMAND + + function testOracleList() public skipped { // TODO: check if this test can be removed for good. + // address ORACLE_WALLET01 = 0x4D6fbF888c374D7964D56144dE0C0cFBd49750D3; + + //assertEq(OsmAbstract(0xF15993A5C5BE496b8e1c9657Fd2233b579Cd3Bc6).wards(ORACLE_WALLET01), 0); + + //_vote(address(spell)); + //_scheduleWaitAndCast(address(spell)); + //assertTrue(spell.done()); + + //assertEq(OsmAbstract(0xF15993A5C5BE496b8e1c9657Fd2233b579Cd3Bc6).wards(ORACLE_WALLET01), 1); + } + + function testRemoveChainlogValues() public { // add the `skipped` modifier to skip + string[1] memory removedKeys = [ + "VOTE_DELEGATE_PROXY_FACTORY" + ]; + + for (uint256 i = 0; i < removedKeys.length; i++) { + try chainLog.getAddress(_stringToBytes32(removedKeys[i])) { + } catch Error(string memory errmsg) { + if (_cmpStr(errmsg, "dss-chain-log/invalid-key")) { + fail(_concat("TestError/key-to-remove-does-not-exist: ", removedKeys[i])); + } else { + fail(errmsg); + } + } + } + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + for (uint256 i = 0; i < removedKeys.length; i++) { + try chainLog.getAddress(_stringToBytes32(removedKeys[i])) { + fail(_concat("TestError/key-not-removed: ", removedKeys[i])); + } catch Error(string memory errmsg) { + assertTrue( + _cmpStr(errmsg, "dss-chain-log/invalid-key"), + _concat("TestError/key-not-removed: ", removedKeys[i]) + ); + } catch { + fail(_concat("TestError/unknown-reason: ", removedKeys[i])); + } + } + } + + function testCollateralIntegrations() public skipped { // add the `skipped` modifier to skip + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + // Insert new collateral tests here + _checkIlkIntegration( + "GNO-A", + GemJoinAbstract(addr.addr("MCD_JOIN_GNO_A")), + ClipAbstract(addr.addr("MCD_CLIP_GNO_A")), + addr.addr("PIP_GNO"), + true, /* _isOSM */ + true, /* _checkLiquidations */ + false /* _transferFee */ + ); + } + + function testIlkClipper() public skipped { // add the `skipped` modifier to skip + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + _checkIlkClipper( + "RETH-A", + GemJoinAbstract(addr.addr("MCD_JOIN_RETH_A")), + ClipAbstract(addr.addr("MCD_CLIP_RETH_A")), + addr.addr("MCD_CLIP_CALC_RETH_A"), + OsmAbstract(addr.addr("PIP_RETH")), + 1_000 * WAD + ); + } + + function testLockstakeIlkIntegration() public { // add the `skipped` modifier to skip + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + _checkLockstakeIlkIntegration( + LockstakeIlkParams({ + ilk: "LSE-MKR-A", + fee: 5_00, + pip: addr.addr("PIP_MKR"), + lsmkr: addr.addr("LOCKSTAKE_MKR"), + engine: addr.addr("LOCKSTAKE_ENGINE"), + clip: addr.addr("LOCKSTAKE_CLIP"), + calc: addr.addr("LOCKSTAKE_CLIP_CALC"), + farm: addr.addr("REWARDS_LSMKR_USDS"), + rToken: addr.addr("USDS"), + rDistr: addr.addr("MCD_SPLIT"), + rDur: 15_649 seconds + }) + ); + } + + function testLerpSurplusBuffer() public skipped { // add the `skipped` modifier to skip + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + // Insert new SB lerp tests here + + LerpAbstract lerp = LerpAbstract(lerpFactory.lerps("NAME")); + + uint256 duration = 210 days; + vm.warp(block.timestamp + duration / 2); + assertEq(vow.hump(), 60 * MILLION * RAD); + lerp.tick(); + assertEq(vow.hump(), 75 * MILLION * RAD); + vm.warp(block.timestamp + duration / 2); + lerp.tick(); + assertEq(vow.hump(), 90 * MILLION * RAD); + assertTrue(lerp.done()); + } + + function testEsmAuth() public skipped { // add the `skipped` modifier to skip + string[1] memory esmAuthorisedContractKeys = [ + "MCD_LITE_PSM_USDC_A_IN_CDT_JAR" + ]; + + for (uint256 i = 0; i < esmAuthorisedContractKeys.length; i++) { + assertEq( + WardsAbstract(addr.addr(_stringToBytes32(esmAuthorisedContractKeys[i]))).wards(address(esm)), + 0, + _concat("TestError/esm-is-ward-before-spell: ", esmAuthorisedContractKeys[i]) + ); + } + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + for (uint256 i = 0; i < esmAuthorisedContractKeys.length; i++) { + assertEq( + WardsAbstract(addr.addr(_stringToBytes32(esmAuthorisedContractKeys[i]))).wards(address(esm)), + 1, + _concat("TestError/esm-is-not-ward-after-spell: ", esmAuthorisedContractKeys[i]) + ); + } + } + + function testOsmReaders() public { // add the `skipped` modifier to skip + address OSM = addr.addr("PIP_MKR"); + address[4] memory newReaders = [ + addr.addr("MCD_SPOT"), + addr.addr("LOCKSTAKE_CLIP"), + addr.addr("CLIPPER_MOM"), + addr.addr("MCD_END") + ]; + + for (uint256 i = 0; i < newReaders.length; i++) { + assertEq(OsmAbstract(OSM).bud(newReaders[i]), 0); + } + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + for (uint256 i = 0; i < newReaders.length; i++) { + assertEq(OsmAbstract(OSM).bud(newReaders[i]), 1); + } + } + + function testMedianReaders() public { // add the `skipped` modifier to skip + address median = chainLog.getAddress("PIP_MKR"); // PIP_MKR before spell + address[1] memory newReaders = [ + addr.addr('PIP_MKR') // PIP_MKR after spell + ]; + + for (uint256 i = 0; i < newReaders.length; i++) { + assertEq(MedianAbstract(median).bud(newReaders[i]), 0); + } + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + for (uint256 i = 0; i < newReaders.length; i++) { + assertEq(MedianAbstract(median).bud(newReaders[i]), 1); + } + } + + struct Authorization { + bytes32 base; + bytes32 ward; + } + + function testNewAuthorizations() public { // add the `skipped` modifier to skip + Authorization[9] memory newAuthorizations = [ + Authorization({ base: "MCD_VAT", ward: "LOCKSTAKE_ENGINE" }), + Authorization({ base: "MCD_VAT", ward: "LOCKSTAKE_CLIP" }), + Authorization({ base: "PIP_MKR", ward: "OSM_MOM" }), + Authorization({ base: "MCD_DOG", ward: "LOCKSTAKE_CLIP" }), + Authorization({ base: "LOCKSTAKE_MKR", ward: "LOCKSTAKE_ENGINE" }), + Authorization({ base: "LOCKSTAKE_ENGINE", ward: "LOCKSTAKE_CLIP" }), + Authorization({ base: "LOCKSTAKE_CLIP", ward: "MCD_DOG" }), + Authorization({ base: "LOCKSTAKE_CLIP", ward: "MCD_END" }), + Authorization({ base: "LOCKSTAKE_CLIP", ward: "CLIPPER_MOM" }) + ]; + + for (uint256 i = 0; i < newAuthorizations.length; i++) { + address base = addr.addr(newAuthorizations[i].base); + address ward = addr.addr(newAuthorizations[i].ward); + assertEq(WardsAbstract(base).wards(ward), 0, _concat("testNewAuthorizations/already-authorized-", newAuthorizations[i].base)); + } + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + for (uint256 i = 0; i < newAuthorizations.length; i++) { + address base = addr.addr(newAuthorizations[i].base); + address ward = addr.addr(newAuthorizations[i].ward); + assertEq(WardsAbstract(base).wards(ward), 1, _concat("testNewAuthorizations/not-authorized-", newAuthorizations[i].base)); + } + } + + function testVestDAI() public skipped { // add the `skipped` modifier to skip + // Provide human-readable names for timestamps + uint256 DEC_01_2023 = 1701385200; + uint256 NOV_30_2024 = 1733007599; + + // For each new stream, provide Stream object + // and initialize the array with the corrent number of new streams + VestStream[] memory streams = new VestStream[](1); + streams[0] = VestStream({ + id: 38, + usr: wallets.addr("ECOSYSTEM_FACILITATOR"), + bgn: DEC_01_2023, + clf: DEC_01_2023, + fin: NOV_30_2024, + tau: 366 days, + mgr: address(0), + res: 1, + tot: 504_000 * WAD, + rxd: 0 + }); + + _checkVestDai(streams); + } + + function testVestMKR() public skipped { // add the `skipped` modifier to skip + // Provide human-readable names for timestamps + uint256 DEC_01_2023 = 1701385200; + uint256 NOV_30_2024 = 1733007599; + + // For each new stream, provide Stream object + // and initialize the array with the corrent number of new streams + VestStream[] memory streams = new VestStream[](1); + streams[0] = VestStream({ + id: 44, + usr: wallets.addr("ECOSYSTEM_FACILITATOR"), + bgn: DEC_01_2023, + clf: DEC_01_2023, + fin: NOV_30_2024, + tau: 366 days, + mgr: address(0), + res: 1, + tot: 216 * WAD, + rxd: 0 + }); + + _checkVestMkr(streams); + } + + function testVestSKY() public skipped { // add the `skipped` modifier to skip + // Provide human-readable names for timestamps + // uint256 DEC_01_2023 = 1701385200; + + // For each new stream, provide Stream object + // and initialize the array with the corrent number of new streams + VestStream[] memory streams = new VestStream[](1); + + // This stream is configured in relative to the spell casting time. + { + uint256 before = vm.snapshot(); + _vote(address(spell)); + spell.schedule(); + vm.warp(spell.nextCastTime()); + + streams[0] = VestStream({ + id: 1, + usr: addr.addr("REWARDS_DIST_USDS_SKY"), + bgn: block.timestamp - 7 days, + clf: block.timestamp - 7 days, + fin: block.timestamp - 7 days + 365 days - 1, + tau: 365 days - 1, + mgr: address(0), + res: 1, + tot: 600_000_000 * WAD, + // Note: the accumulated vested amount is claimed during the spell (`REWARDS_DIST_USDS_SKY.distribute()`) + rxd: 600_000_000 * WAD * 7 days / (365 days - 1) + }); + + vm.revertTo(before); + } + + _checkVestSky(streams); + } + + struct Yank { + uint256 streamId; + address addr; + uint256 finPlanned; + } + + function testYankDAI() public skipped { // add the `skipped` modifier to skip + // Provide human-readable names for timestamps + uint256 FEB_29_2024 = 1709251199; + uint256 MAR_31_2024 = 1711929599; + + // For each yanked stream, provide Yank object with: + // the stream id + // the address of the stream + // the planned fin of the stream (via variable defined above) + // Initialize the array with the corrent number of yanks + Yank[2] memory yanks = [ + Yank(20, wallets.addr("BA_LABS"), FEB_29_2024), + Yank(21, wallets.addr("BA_LABS"), MAR_31_2024) + ]; + + // Test stream id matches `addr` and `fin` + VestAbstract vest = VestAbstract(addr.addr("MCD_VEST_DAI")); // or "MCD_VEST_DAI_LEGACY" + for (uint256 i = 0; i < yanks.length; i++) { + assertEq(vest.usr(yanks[i].streamId), yanks[i].addr, "testYankDAI/unexpected-address"); + assertEq(vest.fin(yanks[i].streamId), yanks[i].finPlanned, "testYankDAI/unexpected-fin-date"); + } + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + for (uint256 i = 0; i < yanks.length; i++) { + // Test stream.fin is set to the current block after the spell + assertEq(vest.fin(yanks[i].streamId), block.timestamp, "testYankDAI/steam-not-yanked"); + } + } + + function testYankMKR() public skipped { // add the `skipped` modifier to skip + // Provide human-readable names for timestamps + uint256 MAR_31_2024 = 1711929599; + + // For each yanked stream, provide Yank object with: + // the stream id + // the address of the stream + // the planned fin of the stream (via variable defined above) + // Initialize the array with the corrent number of yanks + Yank[1] memory yanks = [ + Yank(35, wallets.addr("BA_LABS"), MAR_31_2024) + ]; + + // Test stream id matches `addr` and `fin` + VestAbstract vestTreasury = VestAbstract(addr.addr("MCD_VEST_MKR_TREASURY")); + for (uint256 i = 0; i < yanks.length; i++) { + assertEq(vestTreasury.usr(yanks[i].streamId), yanks[i].addr, "testYankMKR/unexpected-address"); + assertEq(vestTreasury.fin(yanks[i].streamId), yanks[i].finPlanned, "testYankMKR/unexpected-fin-date"); + } + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + for (uint256 i = 0; i < yanks.length; i++) { + // Test stream.fin is set to the current block after the spell + assertEq(vestTreasury.fin(yanks[i].streamId), block.timestamp, "testYankMKR/steam-not-yanked"); + + // Give admin powers to test contract address and make the vesting unrestricted for testing + GodMode.setWard(address(vestTreasury), address(this), 1); + + // Test vest can still be called, making stream "invalid" and not changing `fin` timestamp + vestTreasury.unrestrict(yanks[i].streamId); + vestTreasury.vest(yanks[i].streamId); + assertTrue(!vestTreasury.valid(yanks[i].streamId)); + assertEq(vestTreasury.fin(yanks[i].streamId), block.timestamp, "testYankMKR/steam-fin-changed"); + } + } + + struct Payee { + address token; + address addr; + int256 amount; + } + + struct PaymentAmounts { + int256 dai; + int256 mkr; + int256 usds; + int256 sky; + } + + function testPayments() public { // add the `skipped` modifier to skip + // For each payment, create a Payee object with: + // the address of the transferred token, + // the destination address, + // the amount to be paid + // Initialize the array with the number of payees + Payee[2] memory payees = [ + Payee(address(dai), wallets.addr("AAVE_V3_TREASURY"), 234_089 ether), // Note: ether is only a keyword helper + Payee(address(sky), wallets.addr("EARLY_BIRD_REWARDS"), 27_222_832.80 ether) // Note: ether is only a keyword helper + ]; + // Fill the total values from exec sheet + PaymentAmounts memory expectedTotalDiff = PaymentAmounts({ + dai: 234_089 ether, // Note: ether is only a keyword helper + mkr: 0 ether, // Note: ether is only a keyword helper + usds: 0 ether, // Note: ether is only a keyword helper + sky: 27_222_832.80 ether // Note: ether is only a keyword helper + }); + + // Vote, schedule and warp, but not yet cast (to get correct surplus balance) + _vote(address(spell)); + spell.schedule(); + vm.warp(spell.nextCastTime()); + pot.drip(); + + // Calculate and save previous balances + uint256 previousSurplusBalance = vat.sin(address(vow)); + PaymentAmounts memory previousTotalSupply = PaymentAmounts({ + dai: int256(dai.totalSupply()), + mkr: int256(mkr.totalSupply()), + usds: int256(usds.totalSupply()), + sky: int256(sky.totalSupply()) + }); + PaymentAmounts memory calculatedTotalDiff; + PaymentAmounts[] memory previousPayeeBalances = new PaymentAmounts[](payees.length); + for (uint256 i = 0; i < payees.length; i++) { + if (payees[i].token == address(dai)) { + calculatedTotalDiff.dai += payees[i].amount; + } else if (payees[i].token == address(mkr)) { + calculatedTotalDiff.mkr += payees[i].amount; + } else if (payees[i].token == address(usds)) { + calculatedTotalDiff.usds += payees[i].amount; + } else if (payees[i].token == address(sky)) { + calculatedTotalDiff.sky += payees[i].amount; + } else { + revert('TestPayments/unexpected-payee-token'); + } + previousPayeeBalances[i] = PaymentAmounts({ + dai: int256(dai.balanceOf(payees[i].addr)), + mkr: int256(mkr.balanceOf(payees[i].addr)), + usds: int256(usds.balanceOf(payees[i].addr)), + sky: int256(sky.balanceOf(payees[i].addr)) + }); + } + + // Check calculated vs expected totals + assertEq( + calculatedTotalDiff.dai, + expectedTotalDiff.dai, + "TestPayments/calculated-vs-expected-dai-total-mismatch" + ); + assertEq( + calculatedTotalDiff.usds, + expectedTotalDiff.usds, + "TestPayments/calculated-vs-expected-usds-total-mismatch" + ); + assertEq( + calculatedTotalDiff.mkr, + expectedTotalDiff.mkr, + "TestPayments/calculated-vs-expected-mkr-total-mismatch" + ); + assertEq( + calculatedTotalDiff.sky, + expectedTotalDiff.sky, + "TestPayments/calculated-vs-expected-sky-total-mismatch" + ); + + // Cast spell + spell.cast(); + assertTrue(spell.done(), "TestPayments/spell-not-done"); + + // Check calculated vs actual totals + PaymentAmounts memory actualTotalDiff = PaymentAmounts({ + dai: int256(dai.totalSupply()) - previousTotalSupply.dai, + mkr: int256(mkr.totalSupply()) - previousTotalSupply.mkr, + usds: int256(usds.totalSupply()) - previousTotalSupply.usds, + sky: int256(sky.totalSupply()) - previousTotalSupply.sky + }); + assertEq( + actualTotalDiff.dai + actualTotalDiff.usds, + calculatedTotalDiff.dai + calculatedTotalDiff.usds, + "TestPayments/invalid-dai-usds-total" + ); + assertEq( + actualTotalDiff.mkr * int256(afterSpell.sky_mkr_rate) + actualTotalDiff.sky, + calculatedTotalDiff.mkr * int256(afterSpell.sky_mkr_rate) + calculatedTotalDiff.sky, + "TestPayments/invalid-mkr-sky-total" + ); + // Check that dai/usds transfers modify surplus buffer + assertEq(vat.sin(address(vow)) - previousSurplusBalance, uint256(calculatedTotalDiff.dai + calculatedTotalDiff.usds) * RAY); + + // Check that payees received their payments + for (uint256 i = 0; i < payees.length; i++) { + if (payees[i].token == address(dai)) { + assertEq( + int256(dai.balanceOf(payees[i].addr)), + previousPayeeBalances[i].dai + payees[i].amount, + "TestPayments/invalid-payee-dai-balance" + ); + } else if (payees[i].token == address(mkr)) { + assertEq( + int256(mkr.balanceOf(payees[i].addr)), + previousPayeeBalances[i].mkr + payees[i].amount, + "TestPayments/invalid-payee-mkr-balance" + ); + } else if (payees[i].token == address(usds)) { + assertEq( + int256(usds.balanceOf(payees[i].addr)), + previousPayeeBalances[i].usds + payees[i].amount, + "TestPayments/invalid-payee-usds-balance" + ); + } else if (payees[i].token == address(sky)) { + assertEq( + int256(sky.balanceOf(payees[i].addr)), + previousPayeeBalances[i].sky + payees[i].amount, + "TestPayments/invalid-payee-sky-balance" + ); + } else { + revert('TestPayments/unexpected-payee-token'); + } + } + } + + function testNewCronJobs() public skipped { // add the `skipped` modifier to skip + SequencerLike seq = SequencerLike(addr.addr("CRON_SEQUENCER")); + address[1] memory newJobs = [ + addr.addr("CRON_REWARDS_DIST_JOB") + ]; + + for (uint256 i = 0; i < newJobs.length; i++) { + assertFalse(seq.hasJob(newJobs[i]), "TestError/cron-job-already-in-sequencer"); + } + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + for (uint256 i = 0; i < newJobs.length; i++) { + assertTrue(seq.hasJob(newJobs[i]), "TestError/cron-job-not-added-to-sequencer"); + } + } + + function _setupRootDomain() internal { + vm.makePersistent(address(spell), address(spell.action()), address(addr)); + + string memory root = string.concat(vm.projectRoot(), "/lib/dss-test"); + config = ScriptTools.readInput(root, "integration"); + + rootDomain = new RootDomain(config, getRelativeChain("mainnet")); + } + + function testL2OptimismSpell() public skipped { // TODO: check if this test can be removed for good. + address l2TeleportGateway = BridgeLike( + chainLog.getAddress("OPTIMISM_TELEPORT_BRIDGE") + ).l2TeleportGateway(); + + _setupRootDomain(); + + optimismDomain = new OptimismDomain(config, getRelativeChain("optimism"), rootDomain); + optimismDomain.selectFork(); + + // Check that the L2 Optimism Spell is there and configured + L2Spell optimismSpell = L2Spell(0x9495632F53Cc16324d2FcFCdD4EB59fb88dDab12); + + L2Gateway optimismGateway = L2Gateway(optimismSpell.gateway()); + assertEq(address(optimismGateway), l2TeleportGateway, "l2-optimism-wrong-gateway"); + + bytes32 optDstDomain = optimismSpell.dstDomain(); + assertEq(optDstDomain, bytes32("ETH-MAIN-A"), "l2-optimism-wrong-dst-domain"); + + // Validate pre-spell optimism state + assertEq(optimismGateway.validDomains(optDstDomain), 1, "l2-optimism-invalid-dst-domain"); + // Cast the L1 Spell + rootDomain.selectFork(); + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + // switch to Optimism domain and relay the spell from L1 + // the `true` keeps us on Optimism rather than `rootDomain.selectFork() + optimismDomain.relayFromHost(true); + + // Validate post-spell state + assertEq(optimismGateway.validDomains(optDstDomain), 0, "l2-optimism-invalid-dst-domain"); + } + + function testL2ArbitrumSpell() public skipped { // TODO: check if this test can be removed for good. + // Ensure the Arbitrum Gov Relay has some ETH to pay for the Arbitrum spell + assertGt(chainLog.getAddress("ARBITRUM_GOV_RELAY").balance, 0); + + address l2TeleportGateway = BridgeLike( + chainLog.getAddress("ARBITRUM_TELEPORT_BRIDGE") + ).l2TeleportGateway(); + + _setupRootDomain(); + + arbitrumDomain = new ArbitrumDomain(config, getRelativeChain("arbitrum_one"), rootDomain); + arbitrumDomain.selectFork(); + + // Check that the L2 Arbitrum Spell is there and configured + L2Spell arbitrumSpell = L2Spell(0x852CCBB823D73b3e35f68AD6b14e29B02360FD3d); + + L2Gateway arbitrumGateway = L2Gateway(arbitrumSpell.gateway()); + assertEq(address(arbitrumGateway), l2TeleportGateway, "l2-arbitrum-wrong-gateway"); + + bytes32 arbDstDomain = arbitrumSpell.dstDomain(); + assertEq(arbDstDomain, bytes32("ETH-MAIN-A"), "l2-arbitrum-wrong-dst-domain"); + + // Validate pre-spell arbitrum state + assertEq(arbitrumGateway.validDomains(arbDstDomain), 1, "l2-arbitrum-invalid-dst-domain"); + + // Cast the L1 Spell + rootDomain.selectFork(); + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + // switch to Arbitrum domain and relay the spell from L1 + // the `true` keeps us on Arbitrum rather than `rootDomain.selectFork() + arbitrumDomain.relayFromHost(true); + + // Validate post-spell state + assertEq(arbitrumGateway.validDomains(arbDstDomain), 0, "l2-arbitrum-invalid-dst-domain"); + } + + function testOffboardings() public skipped { // add the `skipped` modifier to skip + uint256 Art; + (Art,,,,) = vat.ilks("USDC-A"); + assertGt(Art, 0); + (Art,,,,) = vat.ilks("PAXUSD-A"); + assertGt(Art, 0); + (Art,,,,) = vat.ilks("GUSD-A"); + assertGt(Art, 0); + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + DssCdpManagerAbstract cdpManager = DssCdpManagerAbstract(addr.addr("CDP_MANAGER")); + + dog.bark("USDC-A", cdpManager.urns(14981), address(0)); + dog.bark("USDC-A", 0x936d9045E7407aBE8acdBaF34EAe4023B44cEfE2, address(0)); + dog.bark("USDC-A", cdpManager.urns(10791), address(0)); + dog.bark("USDC-A", cdpManager.urns(9529), address(0)); + dog.bark("USDC-A", cdpManager.urns(7062), address(0)); + dog.bark("USDC-A", cdpManager.urns(13008), address(0)); + dog.bark("USDC-A", cdpManager.urns(18152), address(0)); + dog.bark("USDC-A", cdpManager.urns(15504), address(0)); + dog.bark("USDC-A", cdpManager.urns(17116), address(0)); + dog.bark("USDC-A", cdpManager.urns(20087), address(0)); + dog.bark("USDC-A", cdpManager.urns(21551), address(0)); + dog.bark("USDC-A", cdpManager.urns(12964), address(0)); + dog.bark("USDC-A", cdpManager.urns(7361), address(0)); + dog.bark("USDC-A", cdpManager.urns(12588), address(0)); + dog.bark("USDC-A", cdpManager.urns(13641), address(0)); + dog.bark("USDC-A", cdpManager.urns(18786), address(0)); + dog.bark("USDC-A", cdpManager.urns(14676), address(0)); + dog.bark("USDC-A", cdpManager.urns(20189), address(0)); + dog.bark("USDC-A", cdpManager.urns(15149), address(0)); + dog.bark("USDC-A", cdpManager.urns(7976), address(0)); + dog.bark("USDC-A", cdpManager.urns(16639), address(0)); + dog.bark("USDC-A", cdpManager.urns(8724), address(0)); + dog.bark("USDC-A", cdpManager.urns(7170), address(0)); + dog.bark("USDC-A", cdpManager.urns(7337), address(0)); + dog.bark("USDC-A", cdpManager.urns(14142), address(0)); + dog.bark("USDC-A", cdpManager.urns(12753), address(0)); + dog.bark("USDC-A", cdpManager.urns(9579), address(0)); + dog.bark("USDC-A", cdpManager.urns(14628), address(0)); + dog.bark("USDC-A", cdpManager.urns(15288), address(0)); + dog.bark("USDC-A", cdpManager.urns(16139), address(0)); + dog.bark("USDC-A", cdpManager.urns(12287), address(0)); + dog.bark("USDC-A", cdpManager.urns(11908), address(0)); + dog.bark("USDC-A", cdpManager.urns(8829), address(0)); + dog.bark("USDC-A", cdpManager.urns(7925), address(0)); + dog.bark("USDC-A", cdpManager.urns(10430), address(0)); + dog.bark("USDC-A", cdpManager.urns(11122), address(0)); + dog.bark("USDC-A", cdpManager.urns(12663), address(0)); + dog.bark("USDC-A", cdpManager.urns(9027), address(0)); + dog.bark("USDC-A", cdpManager.urns(8006), address(0)); + dog.bark("USDC-A", cdpManager.urns(12693), address(0)); + dog.bark("USDC-A", cdpManager.urns(7079), address(0)); + dog.bark("USDC-A", cdpManager.urns(12220), address(0)); + dog.bark("USDC-A", cdpManager.urns(8636), address(0)); + dog.bark("USDC-A", cdpManager.urns(8643), address(0)); + dog.bark("USDC-A", cdpManager.urns(6992), address(0)); + dog.bark("USDC-A", cdpManager.urns(7083), address(0)); + dog.bark("USDC-A", cdpManager.urns(7102), address(0)); + dog.bark("USDC-A", cdpManager.urns(7124), address(0)); + dog.bark("USDC-A", cdpManager.urns(7328), address(0)); + dog.bark("USDC-A", cdpManager.urns(8053), address(0)); + dog.bark("USDC-A", cdpManager.urns(12246), address(0)); + dog.bark("USDC-A", cdpManager.urns(7829), address(0)); + dog.bark("USDC-A", cdpManager.urns(8486), address(0)); + dog.bark("USDC-A", cdpManager.urns(8677), address(0)); + dog.bark("USDC-A", cdpManager.urns(8700), address(0)); + dog.bark("USDC-A", cdpManager.urns(9139), address(0)); + dog.bark("USDC-A", cdpManager.urns(9240), address(0)); + dog.bark("USDC-A", cdpManager.urns(9250), address(0)); + dog.bark("USDC-A", cdpManager.urns(9144), address(0)); + dog.bark("USDC-A", cdpManager.urns(9568), address(0)); + dog.bark("USDC-A", cdpManager.urns(10773), address(0)); + dog.bark("USDC-A", cdpManager.urns(11404), address(0)); + dog.bark("USDC-A", cdpManager.urns(11609), address(0)); + dog.bark("USDC-A", cdpManager.urns(11856), address(0)); + dog.bark("USDC-A", cdpManager.urns(12355), address(0)); + dog.bark("USDC-A", cdpManager.urns(12778), address(0)); + dog.bark("USDC-A", cdpManager.urns(12632), address(0)); + dog.bark("USDC-A", cdpManager.urns(12747), address(0)); + dog.bark("USDC-A", cdpManager.urns(12679), address(0)); + + dog.bark("PAXUSD-A", cdpManager.urns(14896), address(0)); + + vm.store( + address(dog), + bytes32(uint256(keccak256(abi.encode(bytes32("GUSD-A"), uint256(1)))) + 2), + bytes32(type(uint256).max) + ); // Remove GUSD-A hole limit to reach the objective of the testing 0 debt after all barks + dog.bark("GUSD-A", cdpManager.urns(24382), address(0)); + dog.bark("GUSD-A", cdpManager.urns(23939), address(0)); + dog.bark("GUSD-A", cdpManager.urns(25398), address(0)); + + (Art,,,,) = vat.ilks("USDC-A"); + assertEq(Art, 0, "USDC-A Art is not 0"); + (Art,,,,) = vat.ilks("PAXUSD-A"); + assertEq(Art, 0, "PAXUSD-A Art is not 0"); + (Art,,,,) = vat.ilks("GUSD-A"); + assertEq(Art, 0, "GUSD-A Art is not 0"); + } + + function testDaoResolutions() public { // add the `skipped` modifier to skip + // For each resolution, add IPFS hash as item to the resolutions array + // Initialize the array with the number of resolutions + string[1] memory resolutions = [ + "QmYJUvw5xbAJmJknG2xUKDLe424JSTWQQhbJCnucRRjUv7" + ]; + + string memory comma_separated_resolutions = ""; + for (uint256 i = 0; i < resolutions.length; i++) { + comma_separated_resolutions = string.concat(comma_separated_resolutions, resolutions[i]); + if (i + 1 < resolutions.length) { + comma_separated_resolutions = string.concat(comma_separated_resolutions, ","); + } + } + + assertEq(SpellActionLike(spell.action()).dao_resolutions(), comma_separated_resolutions, "dao_resolutions/invalid-format"); + } + + // SPARK TESTS + function testSparkSpellIsExecuted() public { // add the `skipped` modifier to skip + address SPARK_PROXY = addr.addr('SPARK_PROXY'); + address SPARK_SPELL = 0xcc3B9e79261A7064A0f734Cc749A8e3762e0a187; + + vm.expectCall( + SPARK_PROXY, + /* value = */ 0, + abi.encodeCall( + ProxyLike(SPARK_PROXY).exec, + (SPARK_SPELL, abi.encodeWithSignature("execute()")) + ) + ); + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + } + + // SPELL-SPECIFIC TESTS GO BELOW + + function testRwaTellAndCull() public { + bytes32[2] memory ilks = [ + bytes32("RWA007-A"), + bytes32("RWA014-A") + ]; + RwaLiquidationOracleLike oracle = RwaLiquidationOracleLike(addr.addr("MIP21_LIQUIDATION_ORACLE")); + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done()); + + for (uint256 i = 0; i < ilks.length; i++) { + bytes32 ilk = ilks[i]; + (, address pip, uint256 tau, uint256 toc) = oracle.ilks(ilk); + assertGt(toc, 0, _concat("TestError/bad-toc-after-tell-", ilk)); + assertEq(tau, 0, _concat("TestError/bad-tau-after-tell-", ilk)); + assertFalse(oracle.good(ilk), _concat("TestError/still-good-after-tell-", ilk)); + + uint256 price = uint256(DSValueAbstract(pip).read()); + assertEq(price, 0, _concat("TestError/non-zero-oracle-price-after-cull-", ilk)); + + (uint256 Art,, uint256 spot,,) = vat.ilks(ilk); + assertEq(Art, 0, _concat("TestError/non-zero-total-debt-after-cull-", ilk)); + assertEq(spot, 0, _concat("TestError/non-zero-spot-price-after-cull-", ilk)); + } + } + + function testNewOsmMomAddition() public { + bytes32 ilk = "LSE-MKR-A"; + address osm = addr.addr("PIP_MKR"); + + assertEq(osmMom.osms(ilk), address(0), "TestError/osm-already-in-mom"); + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done(), "TestError/spell-not-done"); + + assertEq(osmMom.osms(ilk), osm, "TestError/osm-not-in-mom"); + + assertEq(OsmAbstract(osm).stopped(), 0, "TestError/unexpected-stopped-before"); + vm.prank(chief.hat()); osmMom.stop(ilk); + assertEq(OsmAbstract(osm).stopped(), 1, "TestError/unexpected-stopped-after"); + } +} diff --git a/archive/2024-10-17-DssSpell/dependencies/dss-flappers/FlapperInit.sol b/archive/2024-10-17-DssSpell/dependencies/dss-flappers/FlapperInit.sol new file mode 100644 index 000000000..4d7475efe --- /dev/null +++ b/archive/2024-10-17-DssSpell/dependencies/dss-flappers/FlapperInit.sol @@ -0,0 +1,211 @@ +// SPDX-FileCopyrightText: © 2023 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity >=0.8.0; + +import { DssInstance } from "dss-test/MCD.sol"; +import { SplitterInstance } from "./SplitterInstance.sol"; + +interface FlapperUniV2Like { + function pip() external view returns (address); + function spotter() external view returns (address); + function usds() external view returns (address); + function gem() external view returns (address); + function receiver() external view returns (address); + function pair() external view returns (address); + function rely(address) external; + function file(bytes32, uint256) external; + function file(bytes32, address) external; +} + +interface SplitterMomLike { + function splitter() external view returns (address); + function setAuthority(address) external; +} + +interface OracleWrapperLike { + function pip() external view returns (address); + function divisor() external view returns (uint256); +} + +interface PipLike { + function kiss(address) external; +} + +interface PairLike { + function token0() external view returns (address); + function token1() external view returns (address); +} + +interface UsdsJoinLike { + function dai() external view returns (address); // TODO: Replace when new join is ready by the new getter +} + +interface SplitterLike { + function live() external view returns (uint256); + function vat() external view returns (address); + function usdsJoin() external view returns (address); + function hop() external view returns (uint256); + function rely(address) external; + function file(bytes32, uint256) external; + function file(bytes32, address) external; +} + +interface FarmLike { + function rewardsToken() external view returns (address); + function setRewardsDistribution(address) external; + function setRewardsDuration(uint256) external; +} + +struct FlapperUniV2Config { + uint256 want; + address pip; + address pair; + address usds; + address splitter; + bytes32 prevChainlogKey; + bytes32 chainlogKey; +} + +struct FarmConfig { + address splitter; + address usdsJoin; + uint256 hop; + bytes32 prevChainlogKey; + bytes32 chainlogKey; +} + +struct SplitterConfig { + uint256 hump; + uint256 bump; + uint256 hop; + uint256 burn; + address usdsJoin; + bytes32 splitterChainlogKey; + bytes32 prevMomChainlogKey; + bytes32 momChainlogKey; +} + +library FlapperInit { + uint256 constant WAD = 10 ** 18; + uint256 constant RAY = 10 ** 27; + + function initFlapperUniV2( + DssInstance memory dss, + address flapper_, + FlapperUniV2Config memory cfg + ) internal { + FlapperUniV2Like flapper = FlapperUniV2Like(flapper_); + + // Sanity checks + require(flapper.spotter() == address(dss.spotter), "Flapper spotter mismatch"); + require(flapper.usds() == cfg.usds, "Flapper usds mismatch"); + require(flapper.pair() == cfg.pair, "Flapper pair mismatch"); + require(flapper.receiver() == dss.chainlog.getAddress("MCD_PAUSE_PROXY"), "Flapper receiver mismatch"); + + PairLike pair = PairLike(flapper.pair()); + (address pairUsds, address pairGem) = pair.token0() == cfg.usds ? (pair.token0(), pair.token1()) + : (pair.token1(), pair.token0()); + require(pairUsds == cfg.usds, "Usds mismatch"); + require(pairGem == flapper.gem(), "Gem mismatch"); + + require(cfg.want >= WAD * 90 / 100, "want too low"); + + flapper.file("want", cfg.want); + flapper.file("pip", cfg.pip); + flapper.rely(cfg.splitter); + + SplitterLike(cfg.splitter).file("flapper", flapper_); + + if (cfg.prevChainlogKey != bytes32(0)) dss.chainlog.removeAddress(cfg.prevChainlogKey); + dss.chainlog.setAddress(cfg.chainlogKey, flapper_); + } + + function initDirectOracle(address flapper) internal { + PipLike(FlapperUniV2Like(flapper).pip()).kiss(flapper); + } + + function initOracleWrapper( + DssInstance memory dss, + address wrapper_, + uint256 divisor, + bytes32 clKey + ) internal { + OracleWrapperLike wrapper = OracleWrapperLike(wrapper_); + require(wrapper.divisor() == divisor, "Wrapper divisor mismatch"); // Sanity check + PipLike(wrapper.pip()).kiss(wrapper_); + dss.chainlog.setAddress(clKey, wrapper_); + } + + function setFarm( + DssInstance memory dss, + address farm_, + FarmConfig memory cfg + ) internal { + FarmLike farm = FarmLike(farm_); + SplitterLike splitter = SplitterLike(cfg.splitter); + + require(farm.rewardsToken() == UsdsJoinLike(cfg.usdsJoin).dai(), "Farm rewards not usds"); + // Staking token is checked in the Lockstake script + + // The following two checks enforce the initSplitter function has to be called first + require(cfg.hop >= 5 minutes, "hop too low"); + require(cfg.hop == splitter.hop(), "hop mismatch"); + + splitter.file("farm", farm_); + + farm.setRewardsDistribution(cfg.splitter); + farm.setRewardsDuration(cfg.hop); + + if (cfg.prevChainlogKey != bytes32(0)) dss.chainlog.removeAddress(cfg.prevChainlogKey); + dss.chainlog.setAddress(cfg.chainlogKey, farm_); + } + + function initSplitter( + DssInstance memory dss, + SplitterInstance memory splitterInstance, + SplitterConfig memory cfg + ) internal { + SplitterLike splitter = SplitterLike(splitterInstance.splitter); + SplitterMomLike mom = SplitterMomLike(splitterInstance.mom); + + // Sanity checks + require(splitter.live() == 1, "Splitter not live"); + require(splitter.vat() == address(dss.vat), "Splitter vat mismatch"); + require(splitter.usdsJoin() == cfg.usdsJoin, "Splitter usdsJoin mismatch"); + require(mom.splitter() == splitterInstance.splitter, "Mom splitter mismatch"); + + require(cfg.hump > 0, "hump too low"); + require(cfg.bump % RAY == 0, "bump not multiple of RAY"); + require(cfg.hop >= 5 minutes, "hop too low"); + require(cfg.burn <= WAD, "burn too high"); + + splitter.file("hop", cfg.hop); + splitter.file("burn", cfg.burn); + splitter.rely(address(mom)); + splitter.rely(address(dss.vow)); + + dss.vow.file("flapper", splitterInstance.splitter); + dss.vow.file("hump", cfg.hump); + dss.vow.file("bump", cfg.bump); + + mom.setAuthority(dss.chainlog.getAddress("MCD_ADM")); + + dss.chainlog.setAddress(cfg.splitterChainlogKey, splitterInstance.splitter); + if (cfg.prevMomChainlogKey != bytes32(0)) dss.chainlog.removeAddress(cfg.prevMomChainlogKey); + dss.chainlog.setAddress(cfg.momChainlogKey, address(mom)); + } +} diff --git a/archive/2024-10-17-DssSpell/dependencies/dss-flappers/SplitterInstance.sol b/archive/2024-10-17-DssSpell/dependencies/dss-flappers/SplitterInstance.sol new file mode 100644 index 000000000..bb61fb721 --- /dev/null +++ b/archive/2024-10-17-DssSpell/dependencies/dss-flappers/SplitterInstance.sol @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: © 2023 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity >=0.8.0; + +struct SplitterInstance { + address splitter; + address mom; +} diff --git a/archive/2024-10-17-DssSpell/dependencies/lockstake/LockstakeInit.sol b/archive/2024-10-17-DssSpell/dependencies/lockstake/LockstakeInit.sol new file mode 100644 index 000000000..57809d25d --- /dev/null +++ b/archive/2024-10-17-DssSpell/dependencies/lockstake/LockstakeInit.sol @@ -0,0 +1,259 @@ +// SPDX-FileCopyrightText: © 2023 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity >=0.8.0; + +import { DssInstance } from "dss-test/MCD.sol"; +import { LockstakeInstance } from "./LockstakeInstance.sol"; + +interface LockstakeMkrLike { + function rely(address) external; +} + +interface LockstakeEngineLike { + function voteDelegateFactory() external view returns (address); + function vat() external view returns (address); + function usdsJoin() external view returns (address); + function usds() external view returns (address); + function ilk() external view returns (bytes32); + function mkr() external view returns (address); + function lsmkr() external view returns (address); + function fee() external view returns (uint256); + function mkrSky() external view returns (address); + function sky() external view returns (address); + function rely(address) external; + function file(bytes32, address) external; + function file(bytes32, uint256) external; + function addFarm(address) external; +} + +interface LockstakeClipperLike { + function vat() external view returns (address); + function dog() external view returns (address); + function spotter() external view returns (address); + function engine() external view returns (address); + function ilk() external view returns (bytes32); + function rely(address) external; + function file(bytes32, address) external; + function file(bytes32, uint256) external; + function upchost() external; +} + +interface PipLike { + function kiss(address) external; + function rely(address) external; +} + +interface CalcLike { + function file(bytes32, uint256) external; +} + +interface AutoLineLike { + function setIlk(bytes32, uint256, uint256, uint256) external; +} + +interface OsmMomLike { + function setOsm(bytes32, address) external; +} + +interface LineMomLike { + function addIlk(bytes32) external; +} + +interface ClipperMomLike { + function setPriceTolerance(address, uint256) external; +} + +interface StakingRewardsLike { + function stakingToken() external view returns (address); +} + +interface IlkRegistryLike { + function put( + bytes32 _ilk, + address _join, + address _gem, + uint256 _dec, + uint256 _class, + address _pip, + address _xlip, + string memory _name, + string memory _symbol + ) external; +} + +struct LockstakeConfig { + bytes32 ilk; + address voteDelegateFactory; + address usdsJoin; + address usds; + address mkr; + address mkrSky; + address sky; + address[] farms; + uint256 fee; + uint256 maxLine; + uint256 gap; + uint256 ttl; + uint256 dust; + uint256 duty; + uint256 mat; + uint256 buf; + uint256 tail; + uint256 cusp; + uint256 chip; + uint256 tip; + uint256 stopped; + uint256 chop; + uint256 hole; + uint256 tau; + uint256 cut; + uint256 step; + bool lineMom; + uint256 tolerance; + string name; + string symbol; +} + +library LockstakeInit { + uint256 constant internal RATES_ONE_HUNDRED_PCT = 1000000021979553151239153027; + uint256 constant internal WAD = 10**18; + uint256 constant internal RAY = 10**27; + uint256 constant internal RAD = 10**45; + + function initLockstake( + DssInstance memory dss, + LockstakeInstance memory lockstakeInstance, + LockstakeConfig memory cfg + ) internal { + LockstakeEngineLike engine = LockstakeEngineLike(lockstakeInstance.engine); + LockstakeClipperLike clipper = LockstakeClipperLike(lockstakeInstance.clipper); + CalcLike calc = CalcLike(lockstakeInstance.clipperCalc); + + // Sanity checks + require(engine.voteDelegateFactory() == cfg.voteDelegateFactory, "Engine voteDelegateFactory mismatch"); + require(engine.vat() == address(dss.vat), "Engine vat mismatch"); + require(engine.usdsJoin() == cfg.usdsJoin, "Engine usdsJoin mismatch"); + require(engine.usds() == cfg.usds, "Engine usds mismatch"); + require(engine.ilk() == cfg.ilk, "Engine ilk mismatch"); + require(engine.mkr() == cfg.mkr, "Engine mkr mismatch"); + require(engine.lsmkr() == lockstakeInstance.lsmkr, "Engine lsmkr mismatch"); + require(engine.mkrSky() == cfg.mkrSky, "Engine mkrSky mismatch"); + require(engine.sky() == cfg.sky, "Engine sky mismatch"); + require(clipper.ilk() == cfg.ilk, "Clipper ilk mismatch"); + require(clipper.vat() == address(dss.vat), "Clipper vat mismatch"); + require(clipper.engine() == address(engine), "Clipper engine mismatch"); + require(clipper.dog() == address(dss.dog), "Clipper dog mismatch"); + require(clipper.spotter() == address(dss.spotter), "Clipper spotter mismatch"); + + require(cfg.gap <= cfg.maxLine, "gap greater than max line"); + require(cfg.dust <= cfg.hole, "dust greater than hole"); + require(cfg.duty >= RAY && cfg.duty <= RATES_ONE_HUNDRED_PCT, "duty out of boundaries"); + require(cfg.mat >= RAY && cfg.mat < 10 * RAY, "mat out of boundaries"); + require(cfg.buf >= RAY && cfg.buf < 10 * RAY, "buf out of boundaries"); + require(cfg.cusp < RAY, "cusp negative drop value"); + require(cfg.chip < WAD, "chip equal or greater than 100%"); + require(cfg.tip <= 1_000 * RAD, "tip out of boundaries"); + require(cfg.chop >= WAD && cfg.chop < 2 * WAD, "chop out of boundaries"); + require(cfg.tolerance < RAY, "tolerance equal or greater than 100%"); + + dss.vat.init(cfg.ilk); + dss.vat.file(cfg.ilk, "line", cfg.gap); + dss.vat.file("Line", dss.vat.Line() + cfg.gap); + dss.vat.file(cfg.ilk, "dust", cfg.dust); + dss.vat.rely(address(engine)); + dss.vat.rely(address(clipper)); + + AutoLineLike(dss.chainlog.getAddress("MCD_IAM_AUTO_LINE")).setIlk(cfg.ilk, cfg.maxLine, cfg.gap, cfg.ttl); + + dss.jug.init(cfg.ilk); + dss.jug.file(cfg.ilk, "duty", cfg.duty); + + address pip = dss.chainlog.getAddress("PIP_MKR"); + address clipperMom = dss.chainlog.getAddress("CLIPPER_MOM"); + PipLike(pip).kiss(address(dss.spotter)); + PipLike(pip).kiss(address(clipper)); + PipLike(pip).kiss(clipperMom); + PipLike(pip).kiss(address(dss.end)); + // This assumes pip is a standard Osm sourced by a Median + { + address osmMom = dss.chainlog.getAddress("OSM_MOM"); + PipLike(pip).rely(osmMom); + OsmMomLike(osmMom).setOsm(cfg.ilk, pip); + } + + dss.spotter.file(cfg.ilk, "mat", cfg.mat); + dss.spotter.file(cfg.ilk, "pip", pip); + dss.spotter.poke(cfg.ilk); + + dss.dog.file(cfg.ilk, "clip", address(clipper)); + dss.dog.file(cfg.ilk, "chop", cfg.chop); + dss.dog.file(cfg.ilk, "hole", cfg.hole); + dss.dog.rely(address(clipper)); + + LockstakeMkrLike(lockstakeInstance.lsmkr).rely(address(engine)); + + engine.file("jug", address(dss.jug)); + engine.file("fee", cfg.fee); + for (uint256 i = 0; i < cfg.farms.length; i++) { + require(StakingRewardsLike(cfg.farms[i]).stakingToken() == lockstakeInstance.lsmkr, "Farm staking token mismatch"); + engine.addFarm(cfg.farms[i]); + } + engine.rely(address(clipper)); + + clipper.file("buf", cfg.buf); + clipper.file("tail", cfg.tail); + clipper.file("cusp", cfg.cusp); + clipper.file("chip", cfg.chip); + clipper.file("tip", cfg.tip); + clipper.file("stopped", cfg.stopped); + clipper.file("vow", address(dss.vow)); + clipper.file("calc", address(calc)); + clipper.upchost(); + clipper.rely(address(dss.dog)); + clipper.rely(address(dss.end)); + clipper.rely(clipperMom); + + if (cfg.tau > 0) calc.file("tau", cfg.tau); + if (cfg.cut > 0) calc.file("cut", cfg.cut); + if (cfg.step > 0) calc.file("step", cfg.step); + + if (cfg.lineMom) { + LineMomLike(dss.chainlog.getAddress("LINE_MOM")).addIlk(cfg.ilk); + } + + if (cfg.tolerance > 0) { + ClipperMomLike(clipperMom).setPriceTolerance(address(clipper), cfg.tolerance); + } + + IlkRegistryLike(dss.chainlog.getAddress("ILK_REGISTRY")).put( + cfg.ilk, + address(0), + cfg.mkr, + 18, + 7, // New class + pip, + address(clipper), + cfg.name, + cfg.symbol + ); + + dss.chainlog.setAddress("LOCKSTAKE_MKR", lockstakeInstance.lsmkr); + dss.chainlog.setAddress("LOCKSTAKE_ENGINE", address(engine)); + dss.chainlog.setAddress("LOCKSTAKE_CLIP", address(clipper)); + dss.chainlog.setAddress("LOCKSTAKE_CLIP_CALC", address(calc)); + } +} diff --git a/archive/2024-10-17-DssSpell/dependencies/lockstake/LockstakeInstance.sol b/archive/2024-10-17-DssSpell/dependencies/lockstake/LockstakeInstance.sol new file mode 100644 index 000000000..d249711d0 --- /dev/null +++ b/archive/2024-10-17-DssSpell/dependencies/lockstake/LockstakeInstance.sol @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: © 2023 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity >=0.8.0; + +struct LockstakeInstance { + address lsmkr; + address engine; + address clipper; + address clipperCalc; +} diff --git a/archive/2024-10-17-DssSpell/test/addresses_deployers.sol b/archive/2024-10-17-DssSpell/test/addresses_deployers.sol new file mode 100644 index 000000000..e3ef915a1 --- /dev/null +++ b/archive/2024-10-17-DssSpell/test/addresses_deployers.sol @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: © 2021 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity 0.8.16; + +contract Deployers { + + address[] public addr; + + constructor() { + addr = [ + 0xdDb108893104dE4E1C6d0E47c42237dB4E617ACc, + 0xDa0FaB05039809e63C5D068c897c3e602fA97457, + 0xda0fab060e6cc7b1C0AA105d29Bd50D71f036711, + 0xDA0FaB0700A4389F6E6679aBAb1692B4601ce9bf, + 0x0048d6225D1F3eA4385627eFDC5B4709Cab4A21c, + 0xd200790f62c8da69973e61d4936cfE4f356ccD07, + 0x92723e0bF280942B98bf2d1e832Bde9A3Bd2F2c2, // Chainlog Deployer + 0xdA0C0de01d90A5933692Edf03c7cE946C7c50445, // Old PE + 0xDa0c0De020F80d43dde58c2653aa73d28Df1fBe1, // Old PE + 0xC1E6d8136441FC66612Df3584007f7CB68765e5D, // PE + 0xa22A61c233d7242728b4255420063c92fc1AEBb9, // PE + 0x4D6fbF888c374D7964D56144dE0C0cFBd49750D3, // Oracles + 0x1f42e41A34B71606FcC60b4e624243b365D99745, // Oracles + 0x075da589886BA445d7c7e81c472059dE7AE65250, // Used for Optimism & Arbitrum bridge contracts + 0x7f06941997C7778E7B734fE55f7353f554B06d7d, // Starknet + 0xb27B6fa77D7FBf3C1BD34B0f7DA59b39D3DB0f7e, // CES + 0x39aBD7819E5632Fa06D2ECBba45Dca5c90687EE3, // Oracles from 2022-10-26 + 0x45Ea4FADf8Db54DF5a96774167547893e0b4D6A5, // CES from 2022-10-26 + 0x5C82d7Eafd66d7f5edC2b844860BfD93C3B0474f, // CES from 2022-12-09 + 0x34DBF275E1Df79D1fC7bf6a37feC56A8b1057490, // Sidestream from 2023-05-17 + 0xd1236a6A111879d9862f8374BA15344b6B233Fbd, // Phoenix Labs from 2023-05-24 + 0xfaAD873aDF27bE64D6E27D40Cf2AF0037d39b2eA, // Deployer of FlapperUniv2 + 0xa44E7F0cEfbdA0aEb5fdf6228acA9b9F069CC1F1, // Dewiz from 2024-01-12 + 0x548DAc55f260AA4631F589Cb2fe72b5E9E4C93Dc, // EG_01 + 0x4Ec216c476175a236BD70026b984D4adECa0cfb8, // EG_02 + 0xEAB682cfE848FE2b42DA69a2591369EF589e8F27, // EG_03 + 0x54eAde20f7DD1A67624626A3DB9408185eD0039e, // EG_04 + 0x4E65a603a9170fa572E276D1B70D6295D433bAc5, // EG_05 + 0xD6ec7a1b1f4c42C5208fF68b2436Fab8CC593fB7 // EG_06 + // 0x02416B99202081F6b90851e35682Ca90D547054c. // Deployer for Spark 2023-08-02 + // 0x4953BAe71F6F06b717F7A99DdBe08Cb991412d4D. // Deployer for Spark 2023-08-30 + // 0x04a733f946C0aD8E2773d9A3891A8CCeD900a0F8. // Deployer for Spark 2023-09-13 + ]; + } + + function count() external view returns (uint256) { + return addr.length; + } +} diff --git a/archive/2024-10-17-DssSpell/test/addresses_mainnet.sol b/archive/2024-10-17-DssSpell/test/addresses_mainnet.sol new file mode 100644 index 000000000..87f3bb6bd --- /dev/null +++ b/archive/2024-10-17-DssSpell/test/addresses_mainnet.sol @@ -0,0 +1,522 @@ +// SPDX-FileCopyrightText: © 2020 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity 0.8.16; + +contract Addresses { + + mapping (bytes32 => address) public addr; + + constructor() { + addr["CHANGELOG"] = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; + addr["MULTICALL"] = 0x5e227AD1969Ea493B43F840cfF78d08a6fc17796; + addr["FAUCET"] = 0x0000000000000000000000000000000000000000; + addr["MCD_DEPLOY"] = 0xbaa65281c2FA2baAcb2cb550BA051525A480D3F4; + addr["JOIN_FAB"] = 0xf1738d22140783707Ca71CB3746e0dc7Bf2b0264; + addr["CLIP_FAB"] = 0x0716F25fBaAae9b63803917b6125c10c313dF663; + addr["CALC_FAB"] = 0xE1820A2780193d74939CcA104087CADd6c1aA13A; + addr["LERP_FAB"] = 0x9175561733D138326FDeA86CdFdF53e92b588276; + addr["MCD_GOV"] = 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2; + addr["GOV_GUARD"] = 0x6eEB68B2C7A918f36B78E2DB80dcF279236DDFb8; + addr["MCD_ADM"] = 0x0a3f6849f78076aefaDf113F5BED87720274dDC0; + addr["VOTE_PROXY_FACTORY"] = 0x6FCD258af181B3221073A96dD90D1f7AE7eEc408; + addr["VOTE_DELEGATE_FACTORY_LEGACY"] = 0xD897F108670903D1d6070fcf818f9db3615AF272; + addr["VOTE_DELEGATE_FACTORY"] = 0xC3D809E87A2C9da4F6d98fECea9135d834d6F5A0; + addr["MCD_VAT"] = 0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B; + addr["MCD_JUG"] = 0x19c0976f590D67707E62397C87829d896Dc0f1F1; + addr["MCD_DOG"] = 0x135954d155898D42C90D2a57824C690e0c7BEf1B; + addr["MCD_VOW"] = 0xA950524441892A31ebddF91d3cEEFa04Bf454466; + addr["MCD_JOIN_DAI"] = 0x9759A6Ac90977b93B58547b4A71c78317f391A28; + addr["MCD_FLOP"] = 0xA41B6EF151E06da0e34B009B86E828308986736D; + addr["MCD_PAUSE"] = 0xbE286431454714F511008713973d3B053A2d38f3; + addr["MCD_PAUSE_PROXY"] = 0xBE8E3e3618f7474F8cB1d074A26afFef007E98FB; + addr["MCD_GOV_ACTIONS"] = 0x4F5f0933158569c026d617337614d00Ee6589B6E; + addr["MCD_DAI"] = 0x6B175474E89094C44Da98b954EedeAC495271d0F; + addr["MCD_SPOT"] = 0x65C79fcB50Ca1594B025960e539eD7A9a6D434A3; + addr["MCD_POT"] = 0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7; + addr["MCD_END"] = 0x0e2e8F1D1326A4B9633D96222Ce399c708B19c28; + addr["MCD_CURE"] = 0x0085c9feAb2335447E1F4DC9bf3593a8e28bdfc7; + addr["MCD_ESM"] = 0x09e05fF6142F2f9de8B6B65855A1d56B6cfE4c58; + addr["PROXY_ACTIONS"] = 0x82ecD135Dce65Fbc6DbdD0e4237E0AF93FFD5038; + addr["PROXY_ACTIONS_END"] = 0x7AfF9FC9faD225e3c88cDA06BC56d8Aca774bC57; + addr["PROXY_ACTIONS_DSR"] = 0x07ee93aEEa0a36FfF2A9B95dd22Bd6049EE54f26; + addr["CDP_MANAGER"] = 0x5ef30b9986345249bc32d8928B7ee64DE9435E39; + addr["DSR_MANAGER"] = 0x373238337Bfe1146fb49989fc222523f83081dDb; + addr["GET_CDPS"] = 0x36a724Bd100c39f0Ea4D3A20F7097eE01A8Ff573; + addr["ILK_REGISTRY"] = 0x5a464C28D19848f44199D003BeF5ecc87d090F87; + addr["OSM_MOM"] = 0x76416A4d5190d071bfed309861527431304aA14f; + addr["CLIPPER_MOM"] = 0x79FBDF16b366DFb14F66cE4Ac2815Ca7296405A0; + addr["LINE_MOM"] = 0x9c257e5Aaf73d964aEBc2140CA38078988fB0C10; + addr["PROXY_FACTORY"] = 0xA26e15C895EFc0616177B7c1e7270A4C7D51C997; + addr["PROXY_REGISTRY"] = 0x4678f0a6958e4D2Bc4F1BAF7Bc52E8F3564f3fE4; + addr["MCD_VEST_DAI"] = 0xa4c22f0e25C6630B2017979AcF1f865e94695C4b; + addr["MCD_VEST_DAI_LEGACY"] = 0x2Cc583c0AaCDaC9e23CB601fDA8F1A0c56Cdcb71; + addr["MCD_VEST_MKR"] = 0x0fC8D4f2151453ca0cA56f07359049c8f07997Bd; + addr["MCD_VEST_MKR_TREASURY"] = 0x6D635c8d08a1eA2F1687a5E46b666949c977B7dd; + addr["MCD_FLASH"] = 0x60744434d6339a6B27d73d9Eda62b6F66a0a04FA; + addr["MCD_FLASH_LEGACY"] = 0x1EB4CF3A948E7D72A198fe073cCb8C7a948cD853; + addr["FLASH_KILLER"] = 0x07a4BaAEFA236A649880009B5a2B862097D9a1cD; + addr["PROXY_ACTIONS_CROPPER"] = 0xa2f69F8B9B341CFE9BfBb3aaB5fe116C89C95bAF; + addr["PROXY_ACTIONS_END_CROPPER"] = 0x38f7C166B5B22906f04D8471E241151BA45d97Af; + addr["CDP_REGISTRY"] = 0xBe0274664Ca7A68d6b5dF826FB3CcB7c620bADF3; + addr["MCD_CROPPER"] = 0x8377CD01a5834a6EaD3b7efb482f678f2092b77e; + addr["MCD_CROPPER_IMP"] = 0xaFB21A0e9669cdbA539a4c91Bf6B94c5F013c0DE; + addr["PIP_MKR"] = 0x4F94e33D0D74CfF5Ca0D3a66F1A650628551C56b; + addr["ETH"] = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + addr["PIP_ETH"] = 0x81FE72B5A8d1A857d176C3E7d5Bd2679A9B85763; + addr["MCD_JOIN_ETH_A"] = 0x2F0b23f53734252Bda2277357e97e1517d6B042A; + addr["MCD_FLIP_ETH_A"] = 0xF32836B9E1f47a0515c6Ec431592D5EbC276407f; + addr["MCD_CLIP_ETH_A"] = 0xc67963a226eddd77B91aD8c421630A1b0AdFF270; + addr["MCD_CLIP_CALC_ETH_A"] = 0x7d9f92DAa9254Bbd1f479DBE5058f74C2381A898; + addr["MCD_JOIN_ETH_B"] = 0x08638eF1A205bE6762A8b935F5da9b700Cf7322c; + addr["MCD_FLIP_ETH_B"] = 0xD499d71bE9e9E5D236A07ac562F7B6CeacCa624c; + addr["MCD_CLIP_ETH_B"] = 0x71eb894330e8a4b96b8d6056962e7F116F50e06F; + addr["MCD_CLIP_CALC_ETH_B"] = 0x19E26067c4a69B9534adf97ED8f986c49179dE18; + addr["MCD_JOIN_ETH_C"] = 0xF04a5cC80B1E94C69B48f5ee68a08CD2F09A7c3E; + addr["MCD_FLIP_ETH_C"] = 0x7A67901A68243241EBf66beEB0e7b5395582BF17; + addr["MCD_CLIP_ETH_C"] = 0xc2b12567523e3f3CBd9931492b91fe65b240bc47; + addr["MCD_CLIP_CALC_ETH_C"] = 0x1c4fC274D12b2e1BBDF97795193D3148fCDa6108; + addr["BAT"] = 0x0D8775F648430679A709E98d2b0Cb6250d2887EF; + addr["PIP_BAT"] = 0xB4eb54AF9Cc7882DF0121d26c5b97E802915ABe6; + addr["MCD_JOIN_BAT_A"] = 0x3D0B1912B66114d4096F48A8CEe3A56C231772cA; + addr["MCD_FLIP_BAT_A"] = 0xF7C569B2B271354179AaCC9fF1e42390983110BA; + addr["MCD_CLIP_BAT_A"] = 0x3D22e6f643e2F4c563fD9db22b229Cbb0Cd570fb; + addr["MCD_CLIP_CALC_BAT_A"] = 0x2e118153D304a0d9C5838D5FCb70CEfCbEc81DC2; + addr["USDC"] = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + addr["PIP_USDC"] = 0x77b68899b99b686F415d074278a9a16b336085A0; + addr["MCD_JOIN_USDC_A"] = 0xA191e578a6736167326d05c119CE0c90849E84B7; + addr["MCD_FLIP_USDC_A"] = 0xbe359e53038E41a1ffA47DAE39645756C80e557a; + addr["MCD_CLIP_USDC_A"] = 0x046b1A5718da6A226D912cFd306BA19980772908; + addr["MCD_CLIP_CALC_USDC_A"] = 0x00A0F90666c6Cd3E615cF8459A47e89A08817602; + addr["MCD_JOIN_USDC_B"] = 0x2600004fd1585f7270756DDc88aD9cfA10dD0428; + addr["MCD_FLIP_USDC_B"] = 0x77282aD36aADAfC16bCA42c865c674F108c4a616; + addr["MCD_CLIP_USDC_B"] = 0x5590F23358Fe17361d7E4E4f91219145D8cCfCb3; + addr["MCD_CLIP_CALC_USDC_B"] = 0xD6FE411284b92d309F79e502Dd905D7A3b02F561; + addr["MCD_JOIN_PSM_USDC_A"] = 0x0A59649758aa4d66E25f08Dd01271e891fe52199; + addr["MCD_FLIP_PSM_USDC_A"] = 0x507420100393b1Dc2e8b4C8d0F8A13B56268AC99; + addr["MCD_CLIP_PSM_USDC_A"] = 0x66609b4799fd7cE12BA799AD01094aBD13d5014D; + addr["MCD_CLIP_CALC_PSM_USDC_A"] = 0xbeE028b5Fa9eb0aDAC5eeF7E5B13383172b91A4E; + addr["MCD_PSM_USDC_A"] = 0x89B78CfA322F6C5dE0aBcEecab66Aee45393cC5A; + addr["MCD_LITE_PSM_USDC_A"] = 0xf6e72Db5454dd049d0788e411b06CfAF16853042; + addr["MCD_LITE_PSM_USDC_A_POCKET"] = 0x37305B1cD40574E4C5Ce33f8e8306Be057fD7341; + addr["MCD_LITE_PSM_USDC_A_JAR"] = 0x69cA348Bd928A158ADe7aa193C133f315803b06e; + addr["MCD_LITE_PSM_USDC_A_IN_CDT_JAR"] = 0x5eeB3D8D60B06a44f6124a84EeE7ec0bB747BE6d; + addr["LITE_PSM_MOM"] = 0x467b32b0407Ad764f56304420Cddaa563bDab425; + addr["WBTC"] = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; + addr["PIP_WBTC"] = 0xf185d0682d50819263941e5f4EacC763CC5C6C42; + addr["MCD_JOIN_WBTC_A"] = 0xBF72Da2Bd84c5170618Fbe5914B0ECA9638d5eb5; + addr["MCD_FLIP_WBTC_A"] = 0x58CD24ac7322890382eE45A3E4F903a5B22Ee930; + addr["MCD_CLIP_WBTC_A"] = 0x0227b54AdbFAEec5f1eD1dFa11f54dcff9076e2C; + addr["MCD_CLIP_CALC_WBTC_A"] = 0x5f4CEa97ca1030C6Bd38429c8a0De7Cd4981C70A; + addr["MCD_JOIN_WBTC_B"] = 0xfA8c996e158B80D77FbD0082BB437556A65B96E0; + addr["MCD_CLIP_WBTC_B"] = 0xe30663C6f83A06eDeE6273d72274AE24f1084a22; + addr["MCD_CLIP_CALC_WBTC_B"] = 0xeb911E99D7ADD1350DC39d84D60835BA9B287D96; + addr["MCD_JOIN_WBTC_C"] = 0x7f62f9592b823331E012D3c5DdF2A7714CfB9de2; + addr["MCD_CLIP_WBTC_C"] = 0x39F29773Dcb94A32529d0612C6706C49622161D1; + addr["MCD_CLIP_CALC_WBTC_C"] = 0x4fa2A328E7f69D023fE83454133c273bF5ACD435; + addr["TUSD"] = 0x0000000000085d4780B73119b644AE5ecd22b376; + addr["PIP_TUSD"] = 0xeE13831ca96d191B688A670D47173694ba98f1e5; + addr["MCD_JOIN_TUSD_A"] = 0x4454aF7C8bb9463203b66C816220D41ED7837f44; + addr["MCD_FLIP_TUSD_A"] = 0x9E4b213C4defbce7564F2Ac20B6E3bF40954C440; + addr["MCD_CLIP_TUSD_A"] = 0x0F6f88f8A4b918584E3539182793a0C276097f44; + addr["MCD_CLIP_CALC_TUSD_A"] = 0x9B207AfAAAD1ae300Ea659e71306a7Bd6D81C160; + addr["ZRX"] = 0xE41d2489571d322189246DaFA5ebDe1F4699F498; + addr["PIP_ZRX"] = 0x7382c066801E7Acb2299aC8562847B9883f5CD3c; + addr["MCD_JOIN_ZRX_A"] = 0xc7e8Cd72BDEe38865b4F5615956eF47ce1a7e5D0; + addr["MCD_FLIP_ZRX_A"] = 0xa4341cAf9F9F098ecb20fb2CeE2a0b8C78A18118; + addr["MCD_CLIP_ZRX_A"] = 0xdc90d461E148552387f3aB3EBEE0Bdc58Aa16375; + addr["MCD_CLIP_CALC_ZRX_A"] = 0xebe5e9D77b9DBBA8907A197f4c2aB00A81fb0C4e; + addr["KNC"] = 0xdd974D5C2e2928deA5F71b9825b8b646686BD200; + addr["PIP_KNC"] = 0xf36B79BD4C0904A5F350F1e4f776B81208c13069; + addr["MCD_JOIN_KNC_A"] = 0x475F1a89C1ED844A08E8f6C50A00228b5E59E4A9; + addr["MCD_FLIP_KNC_A"] = 0x57B01F1B3C59e2C0bdfF3EC9563B71EEc99a3f2f; + addr["MCD_CLIP_KNC_A"] = 0x006Aa3eB5E666D8E006aa647D4afAB212555Ddea; + addr["MCD_CLIP_CALC_KNC_A"] = 0x82c41e2ADE28C066a5D3A1E3f5B444a4075C1584; + addr["MANA"] = 0x0F5D2fB29fb7d3CFeE444a200298f468908cC942; + addr["PIP_MANA"] = 0x8067259EA630601f319FccE477977E55C6078C13; + addr["MCD_JOIN_MANA_A"] = 0xA6EA3b9C04b8a38Ff5e224E7c3D6937ca44C0ef9; + addr["MCD_FLIP_MANA_A"] = 0x0a1D75B4f49BA80724a214599574080CD6B68357; + addr["MCD_CLIP_MANA_A"] = 0xF5C8176E1eB0915359E46DEd16E52C071Bb435c0; + addr["MCD_CLIP_CALC_MANA_A"] = 0xABbCd14FeDbb2D39038327055D9e615e178Fd64D; + addr["USDT"] = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + addr["PIP_USDT"] = 0x7a5918670B0C390aD25f7beE908c1ACc2d314A3C; + addr["MCD_JOIN_USDT_A"] = 0x0Ac6A1D74E84C2dF9063bDDc31699FF2a2BB22A2; + addr["MCD_FLIP_USDT_A"] = 0x667F41d0fDcE1945eE0f56A79dd6c142E37fCC26; + addr["MCD_CLIP_USDT_A"] = 0xFC9D6Dd08BEE324A5A8B557d2854B9c36c2AeC5d; + addr["MCD_CLIP_CALC_USDT_A"] = 0x1Cf3DE6D570291CDB88229E70037d1705d5be748; + addr["PAXUSD"] = 0x8E870D67F660D95d5be530380D0eC0bd388289E1; + addr["PAX"] = 0x8E870D67F660D95d5be530380D0eC0bd388289E1; + addr["PIP_PAXUSD"] = 0x043B963E1B2214eC90046167Ea29C2c8bDD7c0eC; + addr["PIP_PAX"] = 0x043B963E1B2214eC90046167Ea29C2c8bDD7c0eC; + addr["MCD_JOIN_PAXUSD_A"] = 0x7e62B7E279DFC78DEB656E34D6a435cC08a44666; + addr["MCD_FLIP_PAXUSD_A"] = 0x52D5D1C05CC79Fc24A629Cb24cB06C5BE5d766E7; + addr["MCD_CLIP_PAXUSD_A"] = 0xBCb396Cd139D1116BD89562B49b9D1d6c25378B0; + addr["MCD_CLIP_CALC_PAXUSD_A"] = 0xA2a4aeFEd398661B0a873d3782DA121c194a0201; + addr["MCD_JOIN_PSM_PAX_A"] = 0x7bbd8cA5e413bCa521C2c80D8d1908616894Cf21; + addr["MCD_CLIP_PSM_PAX_A"] = 0x5322a3551bc6a1b39d5D142e5e38Dc5B4bc5B3d2; + addr["MCD_CLIP_CALC_PSM_PAX_A"] = 0xC19eAc21A4FccdD30812F5fF5FebFbD6817b7593; + addr["MCD_PSM_PAX_A"] = 0x961Ae24a1Ceba861D1FDf723794f6024Dc5485Cf; + addr["COMP"] = 0xc00e94Cb662C3520282E6f5717214004A7f26888; + addr["PIP_COMP"] = 0xBED0879953E633135a48a157718Aa791AC0108E4; + addr["MCD_JOIN_COMP_A"] = 0xBEa7cDfB4b49EC154Ae1c0D731E4DC773A3265aA; + addr["MCD_FLIP_COMP_A"] = 0x524826F84cB3A19B6593370a5889A58c00554739; + addr["MCD_CLIP_COMP_A"] = 0x2Bb690931407DCA7ecE84753EA931ffd304f0F38; + addr["MCD_CLIP_CALC_COMP_A"] = 0x1f546560EAa70985d962f1562B65D4B182341a63; + addr["LRC"] = 0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD; + addr["PIP_LRC"] = 0x9eb923339c24c40Bef2f4AF4961742AA7C23EF3a; + addr["MCD_JOIN_LRC_A"] = 0x6C186404A7A238D3d6027C0299D1822c1cf5d8f1; + addr["MCD_FLIP_LRC_A"] = 0x7FdDc36dcdC435D8F54FDCB3748adcbBF70f3dAC; + addr["MCD_CLIP_LRC_A"] = 0x81C5CDf4817DBf75C7F08B8A1cdaB05c9B3f70F7; + addr["MCD_CLIP_CALC_LRC_A"] = 0x6856CCA4c881CAf29B6563bA046C7Bb73121fb9d; + addr["LINK"] = 0x514910771AF9Ca656af840dff83E8264EcF986CA; + addr["PIP_LINK"] = 0x9B0C694C6939b5EA9584e9b61C7815E8d97D9cC7; + addr["MCD_JOIN_LINK_A"] = 0xdFccAf8fDbD2F4805C174f856a317765B49E4a50; + addr["MCD_FLIP_LINK_A"] = 0xB907EEdD63a30A3381E6D898e5815Ee8c9fd2c85; + addr["MCD_CLIP_LINK_A"] = 0x832Dd5f17B30078a5E46Fdb8130A68cBc4a74dC0; + addr["MCD_CLIP_CALC_LINK_A"] = 0x7B1696677107E48B152e9Bf400293e98B7D86Eb1; + addr["BAL"] = 0xba100000625a3754423978a60c9317c58a424e3D; + addr["PIP_BAL"] = 0x3ff860c0F28D69F392543A16A397D0dAe85D16dE; + addr["MCD_JOIN_BAL_A"] = 0x4a03Aa7fb3973d8f0221B466EefB53D0aC195f55; + addr["MCD_FLIP_BAL_A"] = 0xb2b9bd446eE5e58036D2876fce62b7Ab7334583e; + addr["MCD_CLIP_BAL_A"] = 0x6AAc067bb903E633A422dE7BE9355E62B3CE0378; + addr["MCD_CLIP_CALC_BAL_A"] = 0x79564a41508DA86721eDaDac07A590b5A51B2c01; + addr["YFI"] = 0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e; + addr["PIP_YFI"] = 0x5F122465bCf86F45922036970Be6DD7F58820214; + addr["MCD_JOIN_YFI_A"] = 0x3ff33d9162aD47660083D7DC4bC02Fb231c81677; + addr["MCD_FLIP_YFI_A"] = 0xEe4C9C36257afB8098059a4763A374a4ECFE28A7; + addr["MCD_CLIP_YFI_A"] = 0x9daCc11dcD0aa13386D295eAeeBBd38130897E6f; + addr["MCD_CLIP_CALC_YFI_A"] = 0x1f206d7916Fd3B1b5B0Ce53d5Cab11FCebc124DA; + addr["GUSD"] = 0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd; + addr["PIP_GUSD"] = 0xf45Ae69CcA1b9B043dAE2C83A5B65Bc605BEc5F5; + addr["MCD_JOIN_GUSD_A"] = 0xe29A14bcDeA40d83675aa43B72dF07f649738C8b; + addr["MCD_FLIP_GUSD_A"] = 0xCAa8D152A8b98229fB77A213BE16b234cA4f612f; + addr["MCD_CLIP_GUSD_A"] = 0xa47D68b9dB0A0361284fA04BA40623fcBd1a263E; + addr["MCD_CLIP_CALC_GUSD_A"] = 0xC287E4e9017259f3b21C86A0Ef7840243eC3f4d6; + addr["MCD_JOIN_PSM_GUSD_A"] = 0x79A0FA989fb7ADf1F8e80C93ee605Ebb94F7c6A5; + addr["MCD_CLIP_PSM_GUSD_A"] = 0xf93CC3a50f450ED245e003BFecc8A6Ec1732b0b2; + addr["MCD_CLIP_CALC_PSM_GUSD_A"] = 0x7f67a68a0ED74Ea89A82eD9F243C159ed43a502a; + addr["MCD_PSM_GUSD_A"] = 0x204659B2Fd2aD5723975c362Ce2230Fba11d3900; + addr["MCD_PSM_GUSD_A_JAR"] = 0xf2E7a5B83525c3017383dEEd19Bb05Fe34a62C27; + addr["MCD_PSM_GUSD_A_INPUT_CONDUIT_JAR"] = 0x6934218d8B3E9ffCABEE8cd80F4c1C4167Afa638; + addr["MCD_PSM_PAX_A_JAR"] = 0x8bF8b5C58bb57Ee9C97D0FEA773eeE042B10a787; + addr["MCD_PSM_PAX_A_INPUT_CONDUIT_JAR"] = 0xDa276Ab5F1505965e0B6cD1B6da2A18CcBB29515; + addr["UNI"] = 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984; + addr["PIP_UNI"] = 0xf363c7e351C96b910b92b45d34190650df4aE8e7; + addr["MCD_JOIN_UNI_A"] = 0x3BC3A58b4FC1CbE7e98bB4aB7c99535e8bA9b8F1; + addr["MCD_FLIP_UNI_A"] = 0xF5b8cD9dB5a0EC031304A7B815010aa7761BD426; + addr["MCD_CLIP_UNI_A"] = 0x3713F83Ee6D138Ce191294C131148176015bC29a; + addr["MCD_CLIP_CALC_UNI_A"] = 0xeA7FE6610e6708E2AFFA202948cA19ace3F580AE; + addr["RENBTC"] = 0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D; + addr["PIP_RENBTC"] = 0xf185d0682d50819263941e5f4EacC763CC5C6C42; + addr["MCD_JOIN_RENBTC_A"] = 0xFD5608515A47C37afbA68960c1916b79af9491D0; + addr["MCD_FLIP_RENBTC_A"] = 0x30BC6eBC27372e50606880a36B279240c0bA0758; + addr["MCD_CLIP_RENBTC_A"] = 0x834719BEa8da68c46484E001143bDDe29370a6A3; + addr["MCD_CLIP_CALC_RENBTC_A"] = 0xcC89F368aad8D424d3e759c1525065e56019a0F4; + addr["AAVE"] = 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9; + addr["PIP_AAVE"] = 0x8Df8f06DC2dE0434db40dcBb32a82A104218754c; + addr["MCD_JOIN_AAVE_A"] = 0x24e459F61cEAa7b1cE70Dbaea938940A7c5aD46e; + addr["MCD_FLIP_AAVE_A"] = 0x16e1b844094c885a37509a8f76c533B5fbFED13a; + addr["MCD_CLIP_AAVE_A"] = 0x8723b74F598DE2ea49747de5896f9034CC09349e; + addr["MCD_CLIP_CALC_AAVE_A"] = 0x76024a8EfFCFE270e089964a562Ece6ea5f3a14C; + addr["MATIC"] = 0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0; + addr["PIP_MATIC"] = 0x8874964279302e6d4e523Fb1789981C39a1034Ba; + addr["MCD_JOIN_MATIC_A"] = 0x885f16e177d45fC9e7C87e1DA9fd47A9cfcE8E13; + addr["MCD_CLIP_MATIC_A"] = 0x29342F530ed6120BDB219D602DaFD584676293d1; + addr["MCD_CLIP_CALC_MATIC_A"] = 0xdF8C347B06a31c6ED11f8213C2366348BFea68dB; + addr["STETH"] = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84; + addr["WSTETH"] = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; + addr["PIP_WSTETH"] = 0xFe7a2aC0B945f12089aEEB6eCebf4F384D9f043F; + addr["MCD_JOIN_WSTETH_A"] = 0x10CD5fbe1b404B7E19Ef964B63939907bdaf42E2; + addr["MCD_CLIP_WSTETH_A"] = 0x49A33A28C4C7D9576ab28898F4C9ac7e52EA457A; + addr["MCD_CLIP_CALC_WSTETH_A"] = 0x15282b886675cc1Ce04590148f456428E87eaf13; + addr["MCD_JOIN_WSTETH_B"] = 0x248cCBf4864221fC0E840F29BB042ad5bFC89B5c; + addr["MCD_CLIP_WSTETH_B"] = 0x3ea60191b7d5990a3544B6Ef79983fD67e85494A; + addr["MCD_CLIP_CALC_WSTETH_B"] = 0x95098b29F579dbEb5c198Db6F30E28F7f3955Fbb; + addr["UNIV2DAIETH"] = 0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11; + addr["PIP_UNIV2DAIETH"] = 0xFc8137E1a45BAF0030563EC4F0F851bd36a85b7D; + addr["MCD_JOIN_UNIV2DAIETH_A"] = 0x2502F65D77cA13f183850b5f9272270454094A08; + addr["MCD_FLIP_UNIV2DAIETH_A"] = 0x57dfd99f45747DD55C1c432Db4aEa07FBd5d2B5c; + addr["MCD_CLIP_UNIV2DAIETH_A"] = 0x9F6981bA5c77211A34B76c6385c0f6FA10414035; + addr["MCD_CLIP_CALC_UNIV2DAIETH_A"] = 0xf738C272D648Cc4565EaFb43c0C5B35BbA3bf29d; + addr["MCD_IAM_AUTO_LINE"] = 0xC7Bdd1F2B16447dcf3dE045C4a039A60EC2f0ba3; + addr["PROXY_PAUSE_ACTIONS"] = 0x6bda13D43B7EDd6CAfE1f70fB98b5d40f61A1370; + addr["PROXY_DEPLOYER"] = 0x1b93556AB8dcCEF01Cd7823C617a6d340f53Fb58; + addr["UNIV2WBTCETH"] = 0xBb2b8038a1640196FbE3e38816F3e67Cba72D940; + addr["MCD_JOIN_UNIV2WBTCETH_A"] = 0xDc26C9b7a8fe4F5dF648E314eC3E6Dc3694e6Dd2; + addr["MCD_FLIP_UNIV2WBTCETH_A"] = 0xbc95e8904d879F371Ac6B749727a0EAfDCd2ACB6; + addr["MCD_CLIP_UNIV2WBTCETH_A"] = 0xb15afaB996904170f87a64Fe42db0b64a6F75d24; + addr["MCD_CLIP_CALC_UNIV2WBTCETH_A"] = 0xC94ee71e909DbE08d63aA9e6EFbc9976751601B4; + addr["PIP_UNIV2WBTCETH"] = 0x8400D2EDb8B97f780356Ef602b1BdBc082c2aD07; + addr["UNIV2USDCETH"] = 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc; + addr["MCD_JOIN_UNIV2USDCETH_A"] = 0x03Ae53B33FeeAc1222C3f372f32D37Ba95f0F099; + addr["MCD_FLIP_UNIV2USDCETH_A"] = 0x48d2C08b93E57701C8ae8974Fc4ADd725222B0BB; + addr["MCD_CLIP_UNIV2USDCETH_A"] = 0x93AE03815BAF1F19d7F18D9116E4b637cc32A131; + addr["MCD_CLIP_CALC_UNIV2USDCETH_A"] = 0x022ff40643e8b94C43f0a1E54f51EF6D070AcbC4; + addr["PIP_UNIV2USDCETH"] = 0xf751f24DD9cfAd885984D1bA68860F558D21E52A; + addr["UNIV2DAIUSDC"] = 0xAE461cA67B15dc8dc81CE7615e0320dA1A9aB8D5; + addr["MCD_JOIN_UNIV2DAIUSDC_A"] = 0xA81598667AC561986b70ae11bBE2dd5348ed4327; + addr["MCD_FLIP_UNIV2DAIUSDC_A"] = 0x4a613f79a250D522DdB53904D87b8f442EA94496; + addr["MCD_CLIP_UNIV2DAIUSDC_A"] = 0x9B3310708af333f6F379FA42a5d09CBAA10ab309; + addr["MCD_CLIP_CALC_UNIV2DAIUSDC_A"] = 0xbEF2ab2aA5CC780A03bccf22AD3320c8CF35af6A; + addr["PIP_UNIV2DAIUSDC"] = 0x25D03C2C928ADE19ff9f4FFECc07d991d0df054B; + addr["UNIV2ETHUSDT"] = 0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852; + addr["MCD_JOIN_UNIV2ETHUSDT_A"] = 0x4aAD139a88D2dd5e7410b408593208523a3a891d; + addr["MCD_FLIP_UNIV2ETHUSDT_A"] = 0x118d5051e70F9EaF3B4a6a11F765185A2Ca0802E; + addr["MCD_CLIP_UNIV2ETHUSDT_A"] = 0x2aC4C9b49051275AcB4C43Ec973082388D015D48; + addr["MCD_CLIP_CALC_UNIV2ETHUSDT_A"] = 0xA475582E3D6Ec35091EaE81da3b423C1B27fa029; + addr["PIP_UNIV2ETHUSDT"] = 0x5f6dD5B421B8d92c59dC6D907C9271b1DBFE3016; + addr["UNIV2LINKETH"] = 0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974; + addr["MCD_JOIN_UNIV2LINKETH_A"] = 0xDae88bDe1FB38cF39B6A02b595930A3449e593A6; + addr["MCD_FLIP_UNIV2LINKETH_A"] = 0xb79f818E3c73FCA387845f892356224CA75eac4b; + addr["MCD_CLIP_UNIV2LINKETH_A"] = 0x6aa0520354d1b84e1C6ABFE64a708939529b619e; + addr["MCD_CLIP_CALC_UNIV2LINKETH_A"] = 0x8aCeC2d937a4A4cAF42565aFbbb05ac242134F14; + addr["PIP_UNIV2LINKETH"] = 0xd7d31e62AE5bfC3bfaa24Eda33e8c32D31a1746F; + addr["UNIV2UNIETH"] = 0xd3d2E2692501A5c9Ca623199D38826e513033a17; + addr["MCD_JOIN_UNIV2UNIETH_A"] = 0xf11a98339FE1CdE648e8D1463310CE3ccC3d7cC1; + addr["MCD_FLIP_UNIV2UNIETH_A"] = 0xe5ED7da0483e291485011D5372F3BF46235EB277; + addr["MCD_CLIP_UNIV2UNIETH_A"] = 0xb0ece6F5542A4577E2f1Be491A937Ccbbec8479e; + addr["MCD_CLIP_CALC_UNIV2UNIETH_A"] = 0xad609Ed16157014EF955C94553E40e94A09049f0; + addr["PIP_UNIV2UNIETH"] = 0x8462A88f50122782Cc96108F476deDB12248f931; + addr["UNIV2WBTCDAI"] = 0x231B7589426Ffe1b75405526fC32aC09D44364c4; + addr["MCD_JOIN_UNIV2WBTCDAI_A"] = 0xD40798267795Cbf3aeEA8E9F8DCbdBA9b5281fcC; + addr["MCD_FLIP_UNIV2WBTCDAI_A"] = 0x172200d12D09C2698Dd918d347155fE6692f5662; + addr["MCD_CLIP_UNIV2WBTCDAI_A"] = 0x4fC53a57262B87ABDa61d6d0DB2bE7E9BE68F6b8; + addr["MCD_CLIP_CALC_UNIV2WBTCDAI_A"] = 0x863AEa7D2c4BF2B5Aa191B057240b6Dc29F532eB; + addr["PIP_UNIV2WBTCDAI"] = 0x5bB72127a196392cf4aC00Cf57aB278394d24e55; + addr["UNIV2AAVEETH"] = 0xDFC14d2Af169B0D36C4EFF567Ada9b2E0CAE044f; + addr["MCD_JOIN_UNIV2AAVEETH_A"] = 0x42AFd448Df7d96291551f1eFE1A590101afB1DfF; + addr["MCD_FLIP_UNIV2AAVEETH_A"] = 0x20D298ca96bf8c2000203B911908DbDc1a8Bac58; + addr["MCD_CLIP_UNIV2AAVEETH_A"] = 0x854b252BA15eaFA4d1609D3B98e00cc10084Ec55; + addr["MCD_CLIP_CALC_UNIV2AAVEETH_A"] = 0x5396e541E1F648EC03faf338389045F1D7691960; + addr["PIP_UNIV2AAVEETH"] = 0x32d8416e8538Ac36272c44b0cd962cD7E0198489; + addr["UNIV2DAIUSDT"] = 0xB20bd5D04BE54f870D5C0d3cA85d82b34B836405; + addr["MCD_JOIN_UNIV2DAIUSDT_A"] = 0xAf034D882169328CAf43b823a4083dABC7EEE0F4; + addr["MCD_FLIP_UNIV2DAIUSDT_A"] = 0xD32f8B8aDbE331eC0CfADa9cfDbc537619622cFe; + addr["MCD_CLIP_UNIV2DAIUSDT_A"] = 0xe4B82Be84391b9e7c56a1fC821f47569B364dd4a; + addr["MCD_CLIP_CALC_UNIV2DAIUSDT_A"] = 0x4E88cE740F6bEa31C2b14134F6C5eB2a63104fcF; + addr["PIP_UNIV2DAIUSDT"] = 0x9A1CD705dc7ac64B50777BcEcA3529E58B1292F1; + addr["MIP21_LIQUIDATION_ORACLE"] = 0x88f88Bb9E66241B73B84f3A6E197FbBa487b1E30; + addr["RWA_TOKEN_FAB"] = 0x2B3a4c18705e99bC29b22222dA7E10b643658552; + addr["RWA001"] = 0x10b2aA5D77Aa6484886d8e244f0686aB319a270d; + addr["PIP_RWA001"] = 0x76A9f30B45F4ebFD60Ce8a1c6e963b1605f7cB6d; + addr["MCD_JOIN_RWA001_A"] = 0x476b81c12Dc71EDfad1F64B9E07CaA60F4b156E2; + addr["RWA001_A_URN"] = 0xa3342059BcDcFA57a13b12a35eD4BBE59B873005; + addr["RWA001_A_INPUT_CONDUIT"] = 0x486C85e2bb9801d14f6A8fdb78F5108a0fd932f2; + addr["RWA001_A_OUTPUT_CONDUIT"] = 0xb3eFb912e1cbC0B26FC17388Dd433Cecd2206C3d; + addr["RWA002"] = 0xAAA760c2027817169D7C8DB0DC61A2fb4c19AC23; + addr["PIP_RWA002"] = 0xd2473237E20Bd52F8E7cE0FD79403A6a82fbAEC8; + addr["MCD_JOIN_RWA002_A"] = 0xe72C7e90bc26c11d45dBeE736F0acf57fC5B7152; + addr["RWA002_A_URN"] = 0x225B3da5BE762Ee52B182157E67BeA0b31968163; + addr["RWA002_A_INPUT_CONDUIT"] = 0x2474F297214E5d96Ba4C81986A9F0e5C260f445D; + addr["RWA002_A_OUTPUT_CONDUIT"] = 0x2474F297214E5d96Ba4C81986A9F0e5C260f445D; + addr["RWA003"] = 0x07F0A80aD7AeB7BfB7f139EA71B3C8f7E17156B9; + addr["PIP_RWA003"] = 0xDeF7E88447F7D129420FC881B2a854ABB52B73B8; + addr["MCD_JOIN_RWA003_A"] = 0x1Fe789BBac5b141bdD795A3Bc5E12Af29dDB4b86; + addr["RWA003_A_URN"] = 0x7bF825718e7C388c3be16CFe9982539A7455540F; + addr["RWA003_A_INPUT_CONDUIT"] = 0x2A9798c6F165B6D60Cfb923Fe5BFD6f338695D9B; + addr["RWA003_A_OUTPUT_CONDUIT"] = 0x2A9798c6F165B6D60Cfb923Fe5BFD6f338695D9B; + addr["RWA004"] = 0x873F2101047A62F84456E3B2B13df2287925D3F9; + addr["PIP_RWA004"] = 0x5eEE1F3d14850332A75324514CcbD2DBC8Bbc566; + addr["MCD_JOIN_RWA004_A"] = 0xD50a8e9369140539D1c2D113c4dC1e659c6242eB; + addr["RWA004_A_URN"] = 0xeF1699548717aa4Cf47aD738316280b56814C821; + addr["RWA004_A_INPUT_CONDUIT"] = 0xe1ed3F588A98bF8a3744f4BF74Fd8540e81AdE3f; + addr["RWA004_A_OUTPUT_CONDUIT"] = 0xe1ed3F588A98bF8a3744f4BF74Fd8540e81AdE3f; + addr["RWA005"] = 0x6DB236515E90fC831D146f5829407746EDdc5296; + addr["PIP_RWA005"] = 0x8E6039C558738eb136833aB50271ae065c700d2B; + addr["MCD_JOIN_RWA005_A"] = 0xA4fD373b93aD8e054970A3d6cd4Fd4C31D08192e; + addr["RWA005_A_URN"] = 0xc40907545C57dB30F01a1c2acB242C7c7ACB2B90; + addr["RWA005_A_INPUT_CONDUIT"] = 0x5b702e1fEF3F556cbe219eE697D7f170A236cc66; + addr["RWA005_A_OUTPUT_CONDUIT"] = 0x5b702e1fEF3F556cbe219eE697D7f170A236cc66; + addr["RWA006"] = 0x4EE03cfBF6E784c462839f5954d60f7C2B60b113; + addr["PIP_RWA006"] = 0xB8AeCF04Fdf22Ef6C0c6b6536896e1F2870C41D3; + addr["MCD_JOIN_RWA006_A"] = 0x5E11E34b6745FeBa9449Ae53c185413d6EdC66BE; + addr["RWA006_A_URN"] = 0x0C185bf5388DdfDB288F4D875265d456D18FD9Cb; + addr["RWA006_A_INPUT_CONDUIT"] = 0x8Fe38D1E4293181273E2e323e4c16e0D1d4861e3; + addr["RWA006_A_OUTPUT_CONDUIT"] = 0x8Fe38D1E4293181273E2e323e4c16e0D1d4861e3; + addr["RWA007"] = 0x078fb926b041a816FaccEd3614Cf1E4bc3C723bD; + addr["PIP_RWA007"] = 0x7bb4BcA758c4006998a2769776D9E4E6D86e0Dab; + addr["MCD_JOIN_RWA007_A"] = 0x476aaD14F42469989EFad0b7A31f07b795FF0621; + addr["RWA007_A_URN"] = 0x481bA2d2e86a1c41427893899B5B0cEae41c6726; + addr["RWA007_A_JAR"] = 0xef1B095F700BE471981aae025f92B03091c3AD47; + addr["RWA007_A_INPUT_CONDUIT"] = 0x58f5e979eF74b60a9e5F955553ab8e0e65ba89c9; + addr["RWA007_A_JAR_INPUT_CONDUIT"] = 0xc8bb4e2B249703640e89265e2Ae7c9D5eA2aF742; + addr["RWA007_A_OUTPUT_CONDUIT"] = 0x701C3a384c613157bf473152844f368F2d6EF191; + addr["RWA007_A_OPERATOR"] = 0x94cfBF071f8be325A5821bFeAe00eEbE9CE7c279; + addr["RWA007_A_COINBASE_CUSTODY"] = 0xC3acf3B96E46Aa35dBD2aA3BD12D23c11295E774; + addr["RWA008"] = 0xb9737098b50d7c536b6416dAeB32879444F59fCA; + addr["PIP_RWA008"] = 0x2623dE50D8A6FdC2f0D583327142210b8b464bfd; + addr["MCD_JOIN_RWA008_A"] = 0x56eDD5067d89D4E65Bf956c49eAF054e6Ff0b262; + addr["RWA008_A_URN"] = 0x495215cabc630830071F80263a908E8826a66121; + addr["RWA008_A_INPUT_CONDUIT"] = 0xa397a23dDA051186F202C67148c90683c413383C; + addr["RWA008_A_OUTPUT_CONDUIT"] = 0x21CF5Ad1311788D762f9035829f81B9f54610F0C; + addr["RWA009"] = 0x8b9734bbaA628bFC0c9f323ba08Ed184e5b88Da2; + addr["PIP_RWA009"] = 0xdc7D370A089797Fe9556A2b0400496eBb3a61E44; + addr["MCD_JOIN_RWA009_A"] = 0xEe0FC514280f09083a32AE906cCbD2FAc4c680FA; + addr["RWA009_A_URN"] = 0x1818EE501cd28e01E058E7C283E178E9e04a1e79; + addr["RWA009_A_JAR"] = 0x6C6d4Be2223B5d202263515351034861dD9aFdb6; + addr["RWA009_A_OUTPUT_CONDUIT"] = 0x508D982e13263Fc8e1b5A4E6bf59b335202e36b4; + addr["RWA009_A_INPUT_CONDUIT_URN_USDC"] = 0x08012Ec53A7fAbf6F33318dfb93C1289886eBBE1; + addr["RWA010"] = 0x20C72C1fdd589C4Aaa8d9fF56a43F3B17BA129f8; + addr["PIP_RWA010"] = 0xfBAa6a09A39D485a5Be9F5ebfe09C602E63b21EF; + addr["MCD_JOIN_RWA010_A"] = 0xde2828c3F7B2161cF2a1711edc36c73C56EA72aE; + addr["RWA010_A_URN"] = 0x4866d5d24CdC6cc094423717663b2D3343d4EFF9; + addr["RWA010_A_OUTPUT_CONDUIT"] = 0x1F5C294EF3Ff2d2Da30ea9EDAd490C28096C91dF; + addr["RWA010_A_INPUT_CONDUIT"] = 0x1F5C294EF3Ff2d2Da30ea9EDAd490C28096C91dF; + addr["RWA011"] = 0x0b126F85285d1786F52FC911AfFaaf0d9253e37a; + addr["PIP_RWA011"] = 0x8bDC64d73da9631C962C4932a391CB78065ce7a9; + addr["MCD_JOIN_RWA011_A"] = 0x9048cb84F46e94Ff312DcC50f131191c399D9bC3; + addr["RWA011_A_URN"] = 0x32C9bBA0841F2557C10d3f0d30092f138251aFE6; + addr["RWA011_A_OUTPUT_CONDUIT"] = 0x8e74e529049bB135CF72276C1845f5bD779749b0; + addr["RWA011_A_INPUT_CONDUIT"] = 0x8e74e529049bB135CF72276C1845f5bD779749b0; + addr["RWA012"] = 0x3c7f1379B5ac286eB3636668dEAe71EaA5f7518c; + addr["PIP_RWA012"] = 0x4FA7c611bD25DA38bC929C2A67290FbE49DDFF56; + addr["MCD_JOIN_RWA012_A"] = 0x75646F68B8c5d8F415891F7204978Efb81ec6410; + addr["RWA012_A_URN"] = 0xB22E9DBF60a5b47c8B2D0D6469548F3C2D036B7E; + addr["RWA012_A_OUTPUT_CONDUIT"] = 0x795b917eBe0a812D406ae0f99D71caf36C307e21; + addr["RWA012_A_INPUT_CONDUIT"] = 0x795b917eBe0a812D406ae0f99D71caf36C307e21; + addr["RWA013"] = 0xD6C7FD4392D328e4a8f8bC50F4128B64f4dB2d4C; + addr["PIP_RWA013"] = 0x69Cf63ed6eD57Ad129bF67EB726Ae1bd293edbB0; + addr["MCD_JOIN_RWA013_A"] = 0x779D0fD012815D4239BAf75140e6B2971BEd5113; + addr["RWA013_A_URN"] = 0x9C170dd80Ee2CA5bfDdF00cbE93e8faB2D05bA6D; + addr["RWA013_A_OUTPUT_CONDUIT"] = 0x615984F33604011Fcd76E9b89803Be3816276E61; + addr["RWA013_A_INPUT_CONDUIT"] = 0x615984F33604011Fcd76E9b89803Be3816276E61; + addr["RWA014"] = 0x75dCa04C4aCC1FfB0AEF940e5b49e2C17416008a; + addr["PIP_RWA014"] = 0xfeDAB3d532Af95b10F064c73bebEF68a0d0A5f36; + addr["MCD_JOIN_RWA014_A"] = 0xAd722E51569EF41861fFf5e11942a8E07c7C309e; + addr["RWA014_A_URN"] = 0xf082566Ac42566cF7B392C8e58116a27eEdcBe63; + addr["RWA014_A_JAR"] = 0x71eC6d5Ee95B12062139311CA1fE8FD698Cbe0Cf; + addr["RWA014_A_INPUT_CONDUIT_URN"] = 0x6B86bA08Bd7796464cEa758061Ac173D0268cf49; + addr["RWA014_A_INPUT_CONDUIT_JAR"] = 0x391470cD3D8307AdC051d878A95Fa9459F800Dbc; + addr["RWA014_A_OUTPUT_CONDUIT"] = 0xD7cBDFdE553DE2063caAfBF230Be135e5DbB5064; + addr["RWA014_A_OPERATOR"] = 0x3064D13712338Ee0E092b66Afb3B054F0b7779CB; + addr["RWA014_A_COINBASE_CUSTODY"] = 0x2E5F1f08EBC01d6136c95a40e19D4c64C0be772c; + addr["RWA015"] = 0xf5E5E706EfC841BeD1D24460Cd04028075cDbfdE; + addr["PIP_RWA015"] = 0xDa28e04514E718271b37c9F36fbaf45b4BF42dF4; + addr["MCD_JOIN_RWA015_A"] = 0x8938988f7B368f74bEBdd3dcd8D6A3bd18C15C0b; + addr["RWA015_A_URN"] = 0xebFDaa143827FD0fc9C6637c3604B75Bbcfb7284; + addr["RWA015_A_JAR"] = 0xc27C3D3130563C1171feCC4F76C217Db603997cf; + addr["RWA015_A_INPUT_CONDUIT_URN_USDC"] = 0xe08cb5E24862eA86328295D5E5c08972203C20D8; + addr["RWA015_A_INPUT_CONDUIT_JAR_USDC"] = 0xB9373C557f3aE8cDdD068c1644ED226CfB18A997; + addr["RWA015_A_INPUT_CONDUIT_URN_GUSD"] = 0xAB80C37cB5b21238D975c2Cea46e0F12b3d84B06; + addr["RWA015_A_INPUT_CONDUIT_JAR_GUSD"] = 0x13C31b41E671401c7BC2bbd44eF33B6E9eaa1E7F; + addr["RWA015_A_INPUT_CONDUIT_URN_PAX"] = 0x4f7f76f31CE6Bb20809aaCE30EfD75217Fbfc217; + addr["RWA015_A_INPUT_CONDUIT_JAR_PAX"] = 0x79Fc3810735959db3C6D4fc64F7F7b5Ce48d1CEc; + addr["RWA015_A_OUTPUT_CONDUIT"] = 0x1E86CB085f249772f7e7443631a87c6BDba2aCEb; + addr["RWA015_A_OPERATOR"] = 0x23a10f09Fac6CCDbfb6d9f0215C795F9591D7476; + addr["RWA015_A_CUSTODY"] = 0x65729807485F6f7695AF863d97D62140B7d69d83; + addr["RWA015_A_CUSTODY_2"] = 0x6759610547a36E9597Ef452aa0B9cace91291a2f; + addr["GUNIV3DAIUSDC1"] = 0xAbDDAfB225e10B90D798bB8A886238Fb835e2053; + addr["PIP_GUNIV3DAIUSDC1"] = 0x7F6d78CC0040c87943a0e0c140De3F77a273bd58; + addr["MCD_JOIN_GUNIV3DAIUSDC1_A"] = 0xbFD445A97e7459b0eBb34cfbd3245750Dba4d7a4; + addr["MCD_CLIP_GUNIV3DAIUSDC1_A"] = 0x5048c5Cd3102026472f8914557A1FD35c8Dc6c9e; + addr["MCD_CLIP_CALC_GUNIV3DAIUSDC1_A"] = 0x25B17065b94e3fDcD97d94A2DA29E7F77105aDd7; + addr["MCD_JOIN_TELEPORT_FW_A"] = 0x41Ca7a7Aa2Be78Cf7CB80C0F4a9bdfBC96e81815; + addr["MCD_ROUTER_TELEPORT_FW_A"] = 0xeEf8B35eD538b6Ef7DbA82236377aDE4204e5115; + addr["MCD_ORACLE_AUTH_TELEPORT_FW_A"] = 0x324a895625E7AE38Fc7A6ae91a71e7E937Caa7e6; + addr["STARKNET_TELEPORT_BRIDGE"] = 0x95D8367B74ef8C5d014ff19C212109E243748e28; + addr["STARKNET_TELEPORT_FEE"] = 0x2123159d2178f07E3899d9d22aad2Fb177B59C48; + addr["STARKNET_DAI_BRIDGE"] = 0x9F96fE0633eE838D0298E8b8980E6716bE81388d; + addr["STARKNET_DAI_BRIDGE_LEGACY"] = 0x659a00c33263d9254Fed382dE81349426C795BB6; + addr["STARKNET_ESCROW"] = 0x0437465dfb5B79726e35F08559B0cBea55bb585C; + addr["STARKNET_ESCROW_MOM"] = 0xc238E3D63DfD677Fa0FA9985576f0945C581A266; + addr["STARKNET_GOV_RELAY"] = 0x2385C60D2756Ed8CA001817fC37FDa216d7466c0; + addr["STARKNET_GOV_RELAY_LEGACY"] = 0x9eed6763BA8D89574af1478748a7FDF8C5236fE0; + addr["STARKNET_CORE"] = 0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4; + addr["OPTIMISM_TELEPORT_BRIDGE"] = 0x920347f49a9dbe50865EB6161C3B2774AC046A7F; + addr["OPTIMISM_TELEPORT_FEE"] = 0xA7C088AAD64512Eff242901E33a516f2381b8823; + addr["OPTIMISM_DAI_BRIDGE"] = 0x10E6593CDda8c58a1d0f14C5164B376352a55f2F; + addr["OPTIMISM_ESCROW"] = 0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65; + addr["OPTIMISM_GOV_RELAY"] = 0x09B354CDA89203BB7B3131CC728dFa06ab09Ae2F; + addr["ARBITRUM_TELEPORT_BRIDGE"] = 0x22218359E78bC34E532B653198894B639AC3ed72; + addr["ARBITRUM_TELEPORT_FEE"] = 0xA7C088AAD64512Eff242901E33a516f2381b8823; + addr["ARBITRUM_DAI_BRIDGE"] = 0xD3B5b60020504bc3489D6949d545893982BA3011; + addr["ARBITRUM_ESCROW"] = 0xA10c7CE4b876998858b1a9E12b10092229539400; + addr["ARBITRUM_GOV_RELAY"] = 0x9ba25c289e351779E0D481Ba37489317c34A899d; + addr["ADAI"] = 0x028171bCA77440897B824Ca71D1c56caC55b68A3; + addr["PIP_ADAI"] = 0x6A858592fC4cBdf432Fc9A1Bc8A0422B99330bdF; + addr["GUNIV3DAIUSDC2"] = 0x50379f632ca68D36E50cfBC8F78fe16bd1499d1e; + addr["PIP_GUNIV3DAIUSDC2"] = 0xcCBa43231aC6eceBd1278B90c3a44711a00F4e93; + addr["MCD_JOIN_GUNIV3DAIUSDC2_A"] = 0xA7e4dDde3cBcEf122851A7C8F7A55f23c0Daf335; + addr["MCD_CLIP_GUNIV3DAIUSDC2_A"] = 0xB55da3d3100C4eBF9De755b6DdC24BF209f6cc06; + addr["MCD_CLIP_CALC_GUNIV3DAIUSDC2_A"] = 0xef051Ca2A2d809ba47ee0FC8caaEd06E3D832225; + addr["CRVV1ETHSTETH"] = 0x06325440D014e39736583c165C2963BA99fAf14E; + addr["PIP_CRVV1ETHSTETH"] = 0xEa508F82728927454bd3ce853171b0e2705880D4; + addr["MCD_JOIN_CRVV1ETHSTETH_A"] = 0x82D8bfDB61404C796385f251654F6d7e92092b5D; + addr["MCD_CLIP_CRVV1ETHSTETH_A"] = 0x1926862F899410BfC19FeFb8A3C69C7Aed22463a; + addr["MCD_CLIP_CALC_CRVV1ETHSTETH_A"] = 0x8a4780acABadcae1a297b2eAe5DeEbd7d50DEeB8; + addr["RETH"] = 0xae78736Cd615f374D3085123A210448E74Fc6393; + addr["PIP_RETH"] = 0xeE7F0b350aA119b3d05DC733a4621a81972f7D47; + addr["MCD_JOIN_RETH_A"] = 0xC6424e862f1462281B0a5FAc078e4b63006bDEBF; + addr["MCD_CLIP_RETH_A"] = 0x27CA5E525ea473eD52Ea9423CD08cCc081d96a98; + addr["MCD_CLIP_CALC_RETH_A"] = 0xc59B62AFC96cf9737F717B5e5815070C0f154396; + addr["DIRECT_HUB"] = 0x12F36cdEA3A28C35aC8C6Cc71D9265c17C74A27F; + addr["DIRECT_MOM"] = 0x1AB3145E281c01a1597c8c62F9f060E8e3E02fAB; + addr["DIRECT_SPK_AAVE_LIDO_USDS_POOL"] = 0xbf674d0cD6841C1d7f9b8E809B967B3C5E867653; + addr["DIRECT_SPK_AAVE_LIDO_USDS_PLAN"] = 0xea2abB24bF40ac97746AFf6daCA0BBF885014b31; + addr["DIRECT_SPK_AAVE_LIDO_USDS_ORACLE"] = 0x9dB0EB29c2819f9AE0A91A6E6f644C35a7493E9b; + addr["DIRECT_COMPV2_DAI_POOL"] = 0x621fE4Fde2617ea8FFadE08D0FF5A862aD287EC2; + addr["DIRECT_COMPV2_DAI_PLAN"] = 0xD0eA20f9f9e64A3582d569c8745DaCD746274AEe; + addr["DIRECT_COMPV2_DAI_ORACLE"] = 0x0e2bf18273c953B54FE0a9dEC5429E67851D9468; + addr["DIRECT_AAVEV2_DAI_POOL"] = 0x66aE0574Eb28B92c82569b293B856BB99f80F040; + addr["DIRECT_AAVEV2_DAI_PLAN"] = 0x5846Aee09298f8F3aB5D837d540232d19e5d5813; + addr["DIRECT_AAVEV2_DAI_ORACLE"] = 0x634051fbA31829E245C616e79E289f89c8B851c2; + addr["DIRECT_SPARK_DAI_POOL"] = 0xAfA2DD8a0594B2B24B59de405Da9338C4Ce23437; + addr["DIRECT_SPARK_DAI_PLAN"] = 0x104FaDbb7e17db1A685bBa61007DfB015206a4D2; + addr["DIRECT_SPARK_DAI_ORACLE"] = 0xCBD53B683722F82Dc82EBa7916065532980d4833; + addr["DIRECT_SPARK_MORPHO_DAI_PLAN"] = 0x374b5f915aaED790CBdd341E6f406910d648fD39; + addr["DIRECT_SPARK_MORPHO_DAI_POOL"] = 0x9C259F14E5d9F35A0434cD3C4abbbcaA2f1f7f7E; + addr["DIRECT_SPARK_MORPHO_DAI_ORACLE"] = 0xA5AA14DEE8c8204e424A55776E53bfff413b02Af; + addr["GNO"] = 0x6810e776880C02933D47DB1b9fc05908e5386b96; + addr["PIP_GNO"] = 0xd800ca44fFABecd159c7889c3bf64a217361AEc8; + addr["MCD_JOIN_GNO_A"] = 0x7bD3f01e24E0f0838788bC8f573CEA43A80CaBB5; + addr["MCD_CLIP_GNO_A"] = 0xd9e758bd239e5d568f44D0A748633f6a8d52CBbb; + addr["MCD_CLIP_CALC_GNO_A"] = 0x17b6D0e4237ea7F880aF5F58257cd232a04171D9; + addr["SPARK_PROXY"] = 0x3300f198988e4C9C63F75dF86De36421f06af8c4; + addr["CRON_SEQUENCER"] = 0x238b4E35dAed6100C6162fAE4510261f88996EC9; + addr["CRON_AUTOLINE_JOB"] = 0x67AD4000e73579B9725eE3A149F85C4Af0A61361; + addr["CRON_LERP_JOB"] = 0x8F8f2FC1F0380B9Ff4fE5c3142d0811aC89E32fB; + addr["CRON_D3M_JOB"] = 0x2Ea4aDE144485895B923466B4521F5ebC03a0AeF; + addr["CRON_CLIPPER_MOM_JOB"] = 0x7E93C4f61C8E8874e7366cDbfeFF934Ed089f9fF; + addr["CRON_ORACLE_JOB"] = 0xe717Ec34b2707fc8c226b34be5eae8482d06ED03; + addr["CRON_FLAP_JOB"] = 0xc32506E9bB590971671b649d9B8e18CB6260559F; + addr["CRON_LITE_PSM_JOB"] = 0x0C86162ba3E507592fC8282b07cF18c7F902C401; + addr["USDS"] = 0xdC035D45d973E3EC169d2276DDab16f1e407384F; + addr["USDS_IMP"] = 0x1923DfeE706A8E78157416C29cBCCFDe7cdF4102; + addr["USDS_JOIN"] = 0x3C0f895007CA717Aa01c8693e59DF1e8C3777FEB; + addr["DAI_USDS"] = 0x3225737a9Bbb6473CB4a45b7244ACa2BeFdB276A; + addr["SUSDS"] = 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD; + addr["SUSDS_IMP"] = 0x4e7991e5C547ce825BdEb665EE14a3274f9F61e0; + addr["SKY"] = 0x56072C95FAA701256059aa122697B133aDEd9279; + addr["MKR_SKY"] = 0xBDcFCA946b6CDd965f99a839e4435Bcdc1bc470B; + addr["UNIV2DAIMKR"] = 0x517F9dD285e75b599234F7221227339478d0FcC8; + addr["UNIV2USDSSKY"] = 0x2621CC0B3F3c079c1Db0E80794AA24976F0b9e3c; + addr["MCD_SPLIT"] = 0xBF7111F13386d23cb2Fba5A538107A73f6872bCF; + addr["SPLITTER_MOM"] = 0xF51a075d468dE7dE3599C1Dc47F5C42d02C9230e; + addr["MCD_FLAP"] = 0x374D9c3d5134052Bc558F432Afa1df6575f07407; + addr["FLAP_SKY_ORACLE"] = 0x61A12E5b1d5E9CC1302a32f0df1B5451DE6AE437; + addr["MCD_VEST_SKY"] = 0xB313Eab3FdE99B2bB4bA9750C2DDFBe2729d1cE9; + addr["REWARDS_USDS_SKY"] = 0x0650CAF159C5A49f711e8169D4336ECB9b950275; + addr["REWARDS_DIST_USDS_SKY"] = 0x2F0C88e935Db5A60DDA73b0B4EAEef55883896d9; + addr["REWARDS_USDS_01"] = 0x10ab606B067C9C461d8893c47C7512472E19e2Ce; + addr["REWARDS_LSMKR_USDS"] = 0x92282235a39bE957fF1f37619fD22A9aE5507CB1; + addr["CRON_REWARDS_DIST_JOB"] = 0x6464C34A02DD155dd0c630CE233DD6e21C24F9A5; + addr["WRAPPER_USDS_LITE_PSM_USDC_A"] = 0xA188EEC8F81263234dA3622A406892F3D630f98c; + addr["LOCKSTAKE_MKR"] = 0xb4e0e45e142101dC3Ed768bac219fC35EDBED295; + addr["LOCKSTAKE_ENGINE"] = 0x2b16C07D5fD5cC701a0a871eae2aad6DA5fc8f12; + addr["LOCKSTAKE_CLIP"] = 0xA85621D35cAf9Cf5C146D2376Ce553D7B78A6239; + addr["LOCKSTAKE_CLIP_CALC"] = 0xf13cF3b39823CcfaE6C2354dA56416C80768474e; + } +} diff --git a/archive/2024-10-17-DssSpell/test/addresses_wallets.sol b/archive/2024-10-17-DssSpell/test/addresses_wallets.sol new file mode 100644 index 000000000..8d0ce71bb --- /dev/null +++ b/archive/2024-10-17-DssSpell/test/addresses_wallets.sol @@ -0,0 +1,200 @@ +// SPDX-FileCopyrightText: © 2021 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity 0.8.16; + +contract Wallets { + + mapping (bytes32 => address) public addr; + + constructor() { + + // Core Units + addr["CES_WALLET"] = 0x25307aB59Cd5d8b4E2C01218262Ddf6a89Ff86da; + addr["CES_OP_WALLET"] = 0xD740882B8616B50d0B317fDFf17Ec3f4f853F44f; + addr["COM_WALLET"] = 0x1eE3ECa7aEF17D1e74eD7C447CcBA61aC76aDbA9; + addr["COM_EF_WALLET"] = 0x99E1696A680c0D9f426Be20400E468089E7FDB0f; + addr["DAIF_WALLET"] = 0x34D8d61050Ef9D2B48Ab00e6dc8A8CA6581c5d63; + addr["DAIF_RESERVE_WALLET"] = 0x5F5c328732c9E52DfCb81067b8bA56459b33921f; + addr["DECO_WALLET"] = 0xF482D1031E5b172D42B2DAA1b6e5Cbf6519596f7; + addr["DIN_WALLET"] = 0x7327Aed0Ddf75391098e8753512D8aEc8D740a1F; + addr["DUX_WALLET"] = 0x5A994D8428CCEbCC153863CCdA9D2Be6352f89ad; + addr["EVENTS_WALLET"] = 0x3D274fbAc29C92D2F624483495C0113B44dBE7d2; + addr["GRO_WALLET"] = 0x7800C137A645c07132886539217ce192b9F0528e; + addr["IS_WALLET"] = 0xd1F2eEf8576736C1EbA36920B957cd2aF07280F4; + addr["ORA_WALLET"] = 0x2d09B7b95f3F312ba6dDfB77bA6971786c5b50Cf; + addr["ORA_GAS"] = 0x2B6180b413511ce6e3DA967Ec503b2Cc19B78Db6; + addr["ORA_GAS_EMERGENCY"] = 0x1A5B692029b157df517b7d21a32c8490b8692b0f; + addr["PE_WALLET"] = 0xe2c16c308b843eD02B09156388Cb240cEd58C01c; + addr["RISK_WALLET"] = 0xb386Bc4e8bAE87c3F67ae94Da36F385C100a370a; + addr["RISK_WALLET_VEST"] = 0x5d67d5B1fC7EF4bfF31967bE2D2d7b9323c1521c; + addr["RWF_WALLET"] = 0x96d7b01Cc25B141520C717fa369844d34FF116ec; + addr["SES_WALLET"] = 0x87AcDD9208f73bFc9207e1f6F0fDE906bcA95cc6; + addr["SF01_WALLET"] = 0x4Af6f22d454581bF31B2473Ebe25F5C6F55E028D; + addr["SH_WALLET"] = 0x955993Df48b0458A01cfB5fd7DF5F5DCa6443550; + addr["SH_MULTISIG"] = 0xc657aC882Fb2D6CcF521801da39e910F8519508d; + addr["SNE_WALLET"] = 0x6D348f18c88D45243705D4fdEeB6538c6a9191F1; + addr["SIDESTREAM_WALLET"] = 0xb1f950a51516a697E103aaa69E152d839182f6Fe; + + // Recognized Delegates + addr["ACREINVEST"] = 0x5b9C98e8A3D9Db6cd4B4B4C1F92D0A551D06F00D; + addr["FEEDBLACKLOOPS"] = 0x80882f2A36d49fC46C3c654F7f9cB9a2Bf0423e1; + addr["FIELDTECHNOLOGIES"] = 0x0988E41C02915Fe1beFA78c556f946E5F20ffBD3; + addr["FLIPFLOPFLAP"] = 0x688d508f3a6B0a377e266405A1583B3316f9A2B3; + addr["GFXLABS"] = 0xa6e8772af29b29B9202a073f8E36f447689BEef6; + addr["JUSTINCASE"] = 0xE070c2dCfcf6C6409202A8a210f71D51dbAe9473; + addr["MAKERMAN"] = 0x9AC6A6B24bCd789Fa59A175c0514f33255e1e6D0; + addr["COLDIRON"] = 0x6634e3555DBF4B149c5AEC99D579A2469015AEca; + addr["MONETSUPPLY"] = 0x4Bd73eeE3d0568Bb7C52DFCad7AD5d47Fff5E2CF; + addr["STABLELAB"] = 0x3B91eBDfBC4B78d778f62632a4004804AC5d2DB0; + addr["FLIPSIDE"] = 0x1ef753934C40a72a60EaB12A68B6f8854439AA78; + addr["PENNBLOCKCHAIN"] = 0x2165D41aF0d8d5034b9c266597c1A415FA0253bd; + addr["CHRISBLEC"] = 0xa3f0AbB4Ba74512b5a736C5759446e9B50FDA170; + addr["BLOCKCHAINCOLUMBIA"] = 0xdC1F98682F4F8a5c6d54F345F448437b83f5E432; + addr["MHONKASALOTEEMULAU"] = 0x97Fb39171ACd7C82c439b6158EA2F71D26ba383d; + addr["LLAMA"] = 0xA519a7cE7B24333055781133B13532AEabfAC81b; + addr["CODEKNIGHT"] = 0xf6006d4cF95d6CB2CD1E24AC215D5BF3bca81e7D; + addr["FRONTIERRESEARCH"] = 0xA2d55b89654079987CF3985aEff5A7Bd44DA15A8; + addr["LBSBLOCKCHAIN"] = 0xB83b3e9C8E3393889Afb272D354A7a3Bd1Fbcf5C; + addr["ONESTONE"] = 0x4eFb12d515801eCfa3Be456B5F348D3CD68f9E8a; + addr["PVL"] = 0x6ebB1A9031177208A4CA50164206BF2Fa5ff7416; + addr["CALBLOCKCHAIN"] = 0x7AE109A63ff4DC852e063a673b40BED85D22E585; + addr["CONSENSYS"] = 0xE78658A8acfE982Fde841abb008e57e6545e38b3; + addr["HKUSTEPI"] = 0x2dA0d746938Efa28C7DC093b1da286b3D8bAC34a; + + // AVCs + addr["IAMMEEOH"] = 0x47f7A5d8D27f259582097E1eE59a07a816982AE9; + addr["ACREDAOS"] = 0xBF9226345F601150F64Ea4fEaAE7E40530763cbd; + addr["SPACEXPONENTIAL"] = 0xFF8eEB643C5bfDf6A925f2a5F9aDC9198AF07b78; + addr["RES"] = 0x8c5c8d76372954922400e4654AF7694e158AB784; + addr["LDF"] = 0xC322E8Ec33e9b0a34c7cD185C616087D9842ad50; + addr["OPENSKY"] = 0x8e67eE3BbEb1743dc63093Af493f67C3c23C6f04; + addr["OPENSKY_2"] = 0xf44f97f4113759E0a57756bE49C0655d490Cf19F; + addr["DAVIDPHELPS"] = 0xd56e3E325133EFEd6B1687C88571b8a91e517ab0; + addr["SEEDLATAMETH"] = 0x0087a081a9B430fd8f688c6ac5dD24421BfB060D; + addr["SEEDLATAMETH_2"] = 0xd43b89621fFd48A8A51704f85fd0C87CbC0EB299; + addr["STABLELAB_2"] = 0xbDE65cf2352ed1Dde959f290E973d0fC5cEDFD08; + addr["FLIPSIDEGOV"] = 0x300901243d6CB2E74c10f8aB4cc89a39cC222a29; + addr["DAI_VINCI"] = 0x9ee47F0f82F1A6F45C4E1D25Ce95C321D8C8356a; + addr["HARMONY_2"] = 0xE20A2e231215e9b7Aa308463F1A7490b2ECE55D3; + addr["FHOMONEYETH"] = 0xdbD5651F71ce83d1f0eD275aC456241890a53C74; + addr["ROOT"] = 0xC74392777443a11Dc26Ce8A3D934370514F38A91; + + // MIP-63 Keeper Network + addr["GELATO_VEST_STREAMING"] = 0x478c7Ce3e1df09130f8D65a23AD80e05b352af62; + addr["GELATO_PAYMENT_ADAPTER"] = 0x0B5a34D084b6A5ae4361de033d1e6255623b41eD; + addr["GELATO_TREASURY"] = 0xbfDC6b9944B7EFdb1e2Bc9D55ae9424a2a55b206; + addr["KEEP3R_VEST_STREAMING"] = 0x37b375e3D418fbECba6b283e704F840AB32f3b3C; + addr["KEEP3R_VEST_STREAMING_LEGACY"] = 0xc6A048550C9553F8Ac20fbdeB06f114c27ECcabb; + addr["KEEP3R_PAYMENT_ADAPTER"] = 0xaeFed819b6657B3960A8515863abe0529Dfc444A; + addr["KEEP3R_TREASURY"] = 0x4DfC6DA2089b0dfCF04788b341197146Ea97f743; + addr["CHAINLINK_AUTOMATION"] = 0x5E9dfc5fe95A0754084fB235D58752274314924b; + addr["CHAINLINK_PAYMENT_ADAPTER"] = 0xfB5e1D841BDA584Af789bDFABe3c6419140EC065; + addr["CHAINLINK_TREASURY"] = 0xBE1cE564574377Acb17C2b7628E4F6dd38067a55; + addr["TECHOPS_VEST_STREAMING"] = 0x5A6007d17302238D63aB21407FF600a67765f982; + + // ETH Amsterdam Event SPF + addr["ETH_AMSTERDAM"] = 0xF34ac684BA2734039772f0C0d77bc2545e819212; + + // Phoenix Labs SPF + addr["PHOENIX_LABS"] = 0xD9847E6b1314f0327F320E43B51ca0AaAD6FF509; + + // Ambassador Program Pilot Multisig + addr["AMBASSADOR_WALLET"] = 0xF411d823a48D18B32e608274Df16a9957fE33E45; + + // Legal Domain Work + addr["BIBTA_WALLET"] = 0x173d85CD1754daD73cfc673944D9C8BF11A01D3F; + addr["MIP65_WALLET"] = 0x29408abeCe474C85a12ce15B05efBB6A1e8587fe; + addr["BLOCKTOWER_WALLET"] = 0x117786ad59BC2f13cf25B2359eAa521acB0aDCD9; + addr["BLOCKTOWER_WALLET_2"] = 0xc4dB894A11B1eACE4CDb794d0753A3cB7A633767; + addr["AAVE_V3_TREASURY"] = 0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c; + + // Responsible Facilitators + addr["GOV_ALPHA"] = 0x01D26f8c5cC009868A4BF66E268c17B057fF7A73; + addr["TECH"] = 0x2dC0420A736D1F40893B9481D8968E4D7424bC0B; + addr["STEAKHOUSE"] = 0xf737C76D2B358619f7ef696cf3F94548fEcec379; + addr["BA_LABS"] = 0xDfe08A40054685E205Ed527014899d1EDe49B892; + addr["JANSKY"] = 0xf3F868534FAD48EF5a228Fe78669cf242745a755; + addr["VOTEWIZARD"] = 0x9E72629dF4fcaA2c2F5813FbbDc55064345431b1; + addr["ECOSYSTEM_FACILITATOR"] = 0xFCa6e196c2ad557E64D9397e283C2AFe57344b75; + + // Ecosystem Actors + addr["PHOENIX_LABS_2"] = 0x115F76A98C2268DaE6c1421eb6B08e4e1dF525dA; + addr["VIRIDIAN_STREAM"] = 0xbB8AA212267477C3dbfF6643E497919ec2E3dEC9; + addr["VIRIDIAN_TRANSFER"] = 0xA1E62c6321eEd0ECFcF2f382c8c82FD940D83c07; + addr["DEWIZ"] = 0xD8665628742cf54BBBB3b00B15d7E7a838a1b53a; + addr["SIDESTREAM"] = 0x87EcaaACEd3A02A37e7075dc45D3fEb49867d135; + addr["PULLUP_LABS"] = 0x42aD911c75d25E21727E45eCa2A9d999D5A7f94c; + addr["CHRONICLE_LABS"] = 0x68D0ca2d5Ac777F6A9b0d1be44332BB3d5981C2f; + addr["JETSTREAM"] = 0xF478A08C41ad06E8D957d5e6B6Bcde7452cEE962; + + // Ecosystem Scope + addr["ECOSYSTEM_SCOPE_WALLET"] = 0x6E51E0b5813152880C1389E3e860e69E06aD04D9; + + // Accessibility Scope + addr["LAUNCH_PROJECT_FUNDING"] = 0x3C5142F28567E6a0F172fd0BaaF1f2847f49D02F; + + // Sky Ecosystem Liquidity Bootstrapping + addr["LIQUIDITY_BOOTSTRAPPING"] = 0xD8507ef0A59f37d15B5D7b630FA6EEa40CE4AFdD; + + // Early Bird Rewards Multisig + addr["EARLY_BIRD_REWARDS"] = 0x14D98650d46BF7679BBD05D4f615A1547C87Bf68; + + // Vest Managers + addr["PULLUP_LABS_VEST_MGR"] = 0x9B6213D350A4AFbda2361b6572A07C90c22002F1; + + // Constitutional Delegates + addr["DEFENSOR"] = 0x9542b441d65B6BF4dDdd3d4D2a66D8dCB9EE07a9; + addr["BONAPUBLICA"] = 0x167c1a762B08D7e78dbF8f24e5C3f1Ab415021D3; + addr["GFXLABS_2"] = 0x9B68c14e936104e9a7a24c712BEecdc220002984; + addr["QGOV"] = 0xB0524D8707F76c681901b782372EbeD2d4bA28a6; + addr["TRUENAME"] = 0x612F7924c367575a0Edf21333D96b15F1B345A5d; + addr["VIGILANT"] = 0x2474937cB55500601BCCE9f4cb0A0A72Dc226F61; + addr["FLIPFLOPFLAP_2"] = 0x3d9751EFd857662f2B007A881e05CfD1D7833484; + addr["PBG"] = 0x8D4df847dB7FfE0B46AF084fE031F7691C6478c2; + addr["UPMAKER"] = 0xbB819DF169670DC71A16F58F55956FE642cc6BcD; + addr["WBC"] = 0xeBcE83e491947aDB1396Ee7E55d3c81414fB0D47; + addr["LIBERTAS"] = 0xE1eBfFa01883EF2b4A9f59b587fFf1a5B44dbb2f; + addr["BANDHAR"] = 0xE83B6a503A94a5b764CCF00667689B3a522ABc21; + addr["PALC"] = 0x78Deac4F87BD8007b9cb56B8d53889ed5374e83A; + addr["HARMONY"] = 0xF4704Aa4Ad22cAA2A3Dd7A7C529B4C32f7A421F2; + addr["NAVIGATOR"] = 0x11406a9CC2e37425F15f920F494A51133ac93072; + addr["JAG"] = 0x58D1ec57E4294E4fe650D1CB12b96AE34349556f; + addr["CLOAKY"] = 0x869b6d5d8FA7f4FFdaCA4D23FFE0735c5eD1F818; + addr["SKYNET"] = 0xd4d1A446cD5976a11bd32D3e815A9F85FED2F9F3; + addr["BLUE"] = 0xb6C09680D822F162449cdFB8248a7D3FC26Ec9Bf; + addr["PIPKIN"] = 0x0E661eFE390aE39f90a58b04CF891044e56DEDB7; + addr["JULIACHANG"] = 0x252abAEe2F4f4b8D39E5F12b163eDFb7fac7AED7; + addr["BYTERON"] = 0xc2982e72D060cab2387Dba96b846acb8c96EfF66; + addr["ROCKY"] = 0xC31637BDA32a0811E39456A59022D2C386cb2C85; + addr["CLOAKY_KOHLA"] = 0xA9D43465B43ab95050140668c87A2106C73CA811; + addr["CLOAKY_ENNOIA"] = 0xA7364a1738D0bB7D1911318Ca3FB3779A8A58D7b; + addr["CLOAKY_KOHLA_2"] = 0x73dFC091Ad77c03F2809204fCF03C0b9dccf8c7a; + + // Protocol Engineering Scope + addr["GOV_SECURITY_ENGINEERING"] = 0x569fAD613887ddd8c1815b56A00005BCA7FDa9C0; + addr["MULTICHAIN_ENGINEERING"] = 0x868B44e8191A2574334deB8E7efA38910df941FA; + + // Whistleblower Bounty + addr["VENICE_TREE"] = 0xCDDd2A697d472d1e8a0B1B188646c756d097b058; + addr["COMPACTER"] = 0xbbd4bC3FE72691663c6ffE984Bcdb6C6E6b3a8Dd; + + // Bug Bounty + addr["IMMUNEFI_COMISSION"] = 0x7119f398b6C06095c6E8964C1f58e7C1BAa79E18; + addr["IMMUNEFI_USER_PAYOUT_2024_05_16"] = 0xa24EC79bdF03bB325F36878573B13AedFEd0717f; + addr["IMMUNEFI_USER_PAYOUT_2024_08_08"] = 0xA4a6B5f005cBd2eD38f49ac496d86d3528C7a1aa; + } +} diff --git a/archive/2024-10-17-DssSpell/test/config.sol b/archive/2024-10-17-DssSpell/test/config.sol new file mode 100644 index 000000000..d43259025 --- /dev/null +++ b/archive/2024-10-17-DssSpell/test/config.sol @@ -0,0 +1,1853 @@ +// SPDX-FileCopyrightText: © 2020 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity 0.8.16; + +contract Config { + + struct SpellValues { + address deployed_spell; + uint256 deployed_spell_created; + uint256 deployed_spell_block; + address[] previous_spells; + bool office_hours_enabled; + uint256 expiration_threshold; + } + + struct SystemValues { + uint256 line_offset; + uint256 pot_dsr; + uint256 susds_ssr; + uint256 pause_delay; + uint256 vow_wait; + uint256 vow_dump; + uint256 vow_sump; + uint256 vow_bump; + uint256 vow_hump_min; + uint256 vow_hump_max; + uint256 split_hop; + uint256 split_burn; + bytes32 split_farm; + uint256 flap_want; + uint256 dog_Hole; + uint256 esm_min; + bytes32 pause_authority; + bytes32 osm_mom_authority; + bytes32 clipper_mom_authority; + bytes32 d3m_mom_authority; + bytes32 line_mom_authority; + bytes32 lite_psm_mom_authority; + bytes32 splitter_mom_authority; + uint256 vest_dai_cap; + uint256 vest_mkr_cap; + uint256 vest_sky_cap; + uint256 sky_mkr_rate; + uint256 ilk_count; + string chainlog_version; + mapping (bytes32 => CollateralValues) collaterals; + } + + struct CollateralValues { + bool aL_enabled; + uint256 aL_line; + uint256 aL_gap; + uint256 aL_ttl; + uint256 line; + uint256 dust; + uint256 pct; + uint256 mat; + bytes32 liqType; + bool liqOn; + uint256 chop; + uint256 dog_hole; + uint256 clip_buf; + uint256 clip_tail; + uint256 clip_cusp; + uint256 clip_chip; + uint256 clip_tip; + uint256 clipper_mom; + uint256 cm_tolerance; + uint256 calc_tau; + uint256 calc_step; + uint256 calc_cut; + bool offboarding; + } + + uint256 constant private THOUSAND = 10 ** 3; + uint256 constant private MILLION = 10 ** 6; + uint256 constant private BILLION = 10 ** 9; + uint256 constant private WAD = 10 ** 18; + + SpellValues spellValues; + SystemValues afterSpell; + + function setValues() public { + // Add spells if there is a need to test prior to their cast() functions + // being called on-chain. They will be executed in order from index 0. + address[] memory prevSpells = new address[](0); + // prevSpells[0] = address(0); + + // + // Values for spell-specific parameters + // + spellValues = SpellValues({ + deployed_spell: address(0xACA9a90C92647e3d3f04095118192DC80C470955), // populate with deployed spell if deployed + deployed_spell_created: 1729197815, // use `make deploy-info tx=` to obtain the timestamp + deployed_spell_block: 20987749, // use `make deploy-info tx=` to obtain the block number + previous_spells: prevSpells, // older spells to ensure are executed first + office_hours_enabled: true, // true if officehours is expected to be enabled in the spell + expiration_threshold: 30 days // Amount of time before spell expires + }); + + // + // Values for all system configuration changes + // + afterSpell.line_offset = 680 * MILLION; // Offset between the global line against the sum of local lines + afterSpell.pot_dsr = 5_50; // In basis points + afterSpell.susds_ssr = 6_50; // In basis points + afterSpell.pause_delay = 16 hours; // In seconds + afterSpell.vow_wait = 156 hours; // In seconds + afterSpell.vow_dump = 250; // In whole Dai units + afterSpell.vow_sump = 50 * THOUSAND; // In whole Dai units + afterSpell.vow_bump = 25 * THOUSAND; // In whole Dai units + afterSpell.vow_hump_min = 60 * MILLION; // In whole Dai units + afterSpell.vow_hump_max = 60 * MILLION; // In whole Dai units + afterSpell.split_hop = 15_649 seconds; // In seconds + afterSpell.split_burn = 70_00; // In basis points + afterSpell.split_farm = "REWARDS_LSMKR_USDS"; // Farm chainlog key + afterSpell.flap_want = 9800; // In basis points + afterSpell.dog_Hole = 150 * MILLION; // In whole Dai units + afterSpell.esm_min = 300 * THOUSAND; // In whole MKR units + afterSpell.pause_authority = "MCD_ADM"; // Pause authority + afterSpell.osm_mom_authority = "MCD_ADM"; // OsmMom authority + afterSpell.clipper_mom_authority = "MCD_ADM"; // ClipperMom authority + afterSpell.d3m_mom_authority = "MCD_ADM"; // D3MMom authority + afterSpell.line_mom_authority = "MCD_ADM"; // LineMom authority + afterSpell.lite_psm_mom_authority = "MCD_ADM"; // LitePsmMom authority + afterSpell.splitter_mom_authority = "MCD_ADM"; // SplitterMom authority + afterSpell.vest_dai_cap = 1 * MILLION * WAD / 30 days; // In WAD Dai per second + afterSpell.vest_mkr_cap = 2_220 * WAD / 365 days; // In WAD MKR per second + afterSpell.vest_sky_cap = 800 * MILLION * WAD / 365 days; // In WAD SKY per second + afterSpell.sky_mkr_rate = 24_000; // In whole SKY/MKR units + afterSpell.ilk_count = 68; // Num expected in system + afterSpell.chainlog_version = "1.19.2"; // String expected in system + + // + // Values for all collateral + // Update when adding or modifying Collateral Values + // + afterSpell.collaterals["ETH-A"] = CollateralValues({ + aL_enabled: true, // DssAutoLine is enabled? + aL_line: 15 * BILLION, // In whole Dai units + aL_gap: 150 * MILLION, // In whole Dai units + aL_ttl: 6 hours, // In seconds + line: 0, // In whole Dai units // Not checked here as there is auto line + dust: 7_500, // In whole Dai units + pct: 6_25, // In basis points + mat: 14500, // In basis points + liqType: "clip", // "" or "flip" or "clip" + liqOn: true, // If liquidations are enabled + chop: 1300, // In basis points + dog_hole: 40 * MILLION, // In whole Dai units + clip_buf: 110_00, // In basis points + clip_tail: 7_200, // In seconds, do not use the 'seconds' keyword + clip_cusp: 45_00, // In basis points + clip_chip: 10, // In basis points + clip_tip: 250, // In whole Dai units + clipper_mom: 1, // 1 if circuit breaker enabled + cm_tolerance: 5000, // In basis points + calc_tau: 0, // In seconds + calc_step: 90, // In seconds + calc_cut: 9900, // In basis points + offboarding: false // If mat is being offboarded + }); + afterSpell.collaterals["ETH-B"] = CollateralValues({ + aL_enabled: true, + aL_line: 250 * MILLION, + aL_gap: 20 * MILLION, + aL_ttl: 6 hours, + line: 0, + dust: 25 * THOUSAND, + pct: 6_75, + mat: 13000, + liqType: "clip", + liqOn: true, + chop: 1300, + dog_hole: 15 * MILLION, + clip_buf: 110_00, + clip_tail: 4_800, + clip_cusp: 45_00, + clip_chip: 10, + clip_tip: 250, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 60, + calc_cut: 9900, + offboarding: false + }); + afterSpell.collaterals["ETH-C"] = CollateralValues({ + aL_enabled: true, + aL_line: 2 * BILLION, + aL_gap: 100 * MILLION, + aL_ttl: 8 hours, + line: 0, + dust: 3_500, + pct: 6_00, + mat: 17000, + liqType: "clip", + liqOn: true, + chop: 1300, + dog_hole: 35 * MILLION, + clip_buf: 110_00, + clip_tail: 7_200, + clip_cusp: 45_00, + clip_chip: 10, + clip_tip: 250, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: false + }); + afterSpell.collaterals["BAT-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 400, + mat: 1120000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 1 * MILLION + 500 * THOUSAND, + clip_buf: 13000, + clip_tail: 140 minutes, + clip_cusp: 4000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["USDC-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 0, + mat: 150000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 20_000_000, + clip_buf: 100_00, + clip_tail: 720 minutes, + clip_cusp: 99_00, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 9500, + calc_tau: 4_320_000, + calc_step: 0, + calc_cut: 0, + offboarding: true + }); + afterSpell.collaterals["USDC-B"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 5000, + mat: 12000, + liqType: "clip", + liqOn: false, + chop: 1300, + dog_hole: 0, + clip_buf: 10500, + clip_tail: 220 minutes, + clip_cusp: 9000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 0, + cm_tolerance: 9500, + calc_tau: 0, + calc_step: 120, + calc_cut: 9990, + offboarding: false + }); + afterSpell.collaterals["WBTC-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 7_500, + pct: 9_25, + mat: 15000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 10 * MILLION, + clip_buf: 110_00, + clip_tail: 7_200, + clip_cusp: 45_00, + clip_chip: 10, + clip_tip: 250, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: false + }); + afterSpell.collaterals["WBTC-B"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 25 * THOUSAND, + pct: 9_75, + mat: 15000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 5 * MILLION, + clip_buf: 110_00, + clip_tail: 4_800, + clip_cusp: 45_00, + clip_chip: 10, + clip_tip: 250, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 60, + calc_cut: 9900, + offboarding: false + }); + afterSpell.collaterals["WBTC-C"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 3_500, + pct: 9_00, + mat: 17500, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 10 * MILLION, + clip_buf: 110_00, + clip_tail: 7_200, + clip_cusp: 45_00, + clip_chip: 10, + clip_tip: 250, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: false + }); + afterSpell.collaterals["TUSD-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 0, + mat: 15000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 5 * MILLION, + clip_buf: 10000, + clip_tail: 120 hours, + clip_cusp: 9800, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 9500, + calc_tau: 250 days, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["KNC-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 500, + mat: 500000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 500 * THOUSAND, + clip_buf: 13000, + clip_tail: 140 minutes, + clip_cusp: 4000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["ZRX-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 400, + mat: 550000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 1 * MILLION, + clip_buf: 13000, + clip_tail: 140 minutes, + clip_cusp: 4000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["MANA-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 5000, + mat: 17500, + liqType: "clip", + liqOn: true, + chop: 3000, + dog_hole: 1 * MILLION, + clip_buf: 120_00, + clip_tail: 140 minutes, + clip_cusp: 40_00, + clip_chip: 10, + clip_tip: 250, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: false + }); + afterSpell.collaterals["USDT-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 800, + mat: 30000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 15_000, + clip_buf: 10500, + clip_tail: 220 minutes, + clip_cusp: 9000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 9500, + calc_tau: 0, + calc_step: 120, + calc_cut: 9990, + offboarding: false + }); + afterSpell.collaterals["PAXUSD-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 0, + mat: 150000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 3_000_000, + clip_buf: 100_00, + clip_tail: 720 minutes, + clip_cusp: 99_00, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 9500, + calc_tau: 4_320_000, + calc_step: 0, + calc_cut: 0, + offboarding: true + }); + afterSpell.collaterals["COMP-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 100, + mat: 200000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 2 * MILLION, + clip_buf: 13000, + clip_tail: 140 minutes, + clip_cusp: 4000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["LRC-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 400, + mat: 2430000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 500 * THOUSAND, + clip_buf: 13000, + clip_tail: 140 minutes, + clip_cusp: 4000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["LINK-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 250, + mat: 10000_00, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 3 * MILLION, + clip_buf: 120_00, + clip_tail: 140 minutes, + clip_cusp: 40_00, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["BAL-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 100, + mat: 230000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 3 * MILLION, + clip_buf: 13000, + clip_tail: 140 minutes, + clip_cusp: 4000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["YFI-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 150, + mat: 10000_00, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 1 * MILLION, + clip_buf: 130_00, + clip_tail: 140 minutes, + clip_cusp: 40_00, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["GUSD-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 100, + mat: 150000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 300_000, + clip_buf: 100_00, + clip_tail: 720 minutes, + clip_cusp: 99_00, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 9500, + calc_tau: 4_320_000, + calc_step: 0, + calc_cut: 0, + offboarding: true + }); + afterSpell.collaterals["UNI-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 300, + mat: 1300_00, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 5 * MILLION, + clip_buf: 13000, + clip_tail: 140 minutes, + clip_cusp: 4000, + clip_chip: 10, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["RENBTC-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 225, + mat: 5000_00, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 350 * THOUSAND, + clip_buf: 120_00, + clip_tail: 140 minutes, + clip_cusp: 40_00, + clip_chip: 10, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["AAVE-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 100, + mat: 210000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 5 * MILLION, + clip_buf: 13000, + clip_tail: 140 minutes, + clip_cusp: 4000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["UNIV2DAIETH-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 60 * THOUSAND, + pct: 100, + mat: 2000_00, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 5 * MILLION, + clip_buf: 11500, + clip_tail: 215 minutes, + clip_cusp: 6000, + clip_chip: 10, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 7000, + calc_tau: 0, + calc_step: 125, + calc_cut: 9950, + offboarding: true + }); + afterSpell.collaterals["PSM-USDC-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 0, + mat: 10000, + liqType: "clip", + liqOn: false, + chop: 1300, + dog_hole: 0, + clip_buf: 10500, + clip_tail: 220 minutes, + clip_cusp: 9000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 0, + cm_tolerance: 9500, + calc_tau: 0, + calc_step: 120, + calc_cut: 9990, + offboarding: false + }); + afterSpell.collaterals["LITE-PSM-USDC-A"] = CollateralValues({ + aL_enabled: true, + aL_line: 10 * BILLION, + aL_gap: 400 * MILLION, + aL_ttl: 12 hours, + line: 0, + dust: 0, + pct: 0, + mat: 100_00, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["UNIV2WBTCETH-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 25 * THOUSAND, + pct: 200, + mat: 2400_00, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 5 * MILLION, + clip_buf: 13000, + clip_tail: 200 minutes, + clip_cusp: 4000, + clip_chip: 10, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 130, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["UNIV2USDCETH-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 60 * THOUSAND, + pct: 150, + mat: 10000_00, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 5 * MILLION, + clip_buf: 11500, + clip_tail: 215 minutes, + clip_cusp: 6000, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 7000, + calc_tau: 0, + calc_step: 125, + calc_cut: 9950, + offboarding: true + }); + afterSpell.collaterals["UNIV2DAIUSDC-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 2, + mat: 10200, + liqType: "clip", + liqOn: false, + chop: 1300, + dog_hole: 0, + clip_buf: 10500, + clip_tail: 220 minutes, + clip_cusp: 9000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 0, + cm_tolerance: 9500, + calc_tau: 0, + calc_step: 120, + calc_cut: 9990, + offboarding: false + }); + afterSpell.collaterals["UNIV2ETHUSDT-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 200, + mat: 14000, + liqType: "clip", + liqOn: true, + chop: 1300, + dog_hole: 5 * MILLION, + clip_buf: 11500, + clip_tail: 215 minutes, + clip_cusp: 6000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 7000, + calc_tau: 0, + calc_step: 125, + calc_cut: 9950, + offboarding: false + }); + afterSpell.collaterals["UNIV2LINKETH-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 300, + mat: 160000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 3 * MILLION, + clip_buf: 13000, + clip_tail: 200 minutes, + clip_cusp: 4000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 130, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["UNIV2UNIETH-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 25 * THOUSAND, + pct: 400, + mat: 16000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 3 * MILLION, + clip_buf: 13000, + clip_tail: 200 minutes, + clip_cusp: 4000, + clip_chip: 10, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 130, + calc_cut: 9900, + offboarding: false + }); + afterSpell.collaterals["UNIV2WBTCDAI-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 60 * THOUSAND, + pct: 0, + mat: 800_00, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 5 * MILLION, + clip_buf: 11500, + clip_tail: 215 minutes, + clip_cusp: 6000, + clip_chip: 10, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 7000, + calc_tau: 0, + calc_step: 125, + calc_cut: 9950, + offboarding: true + }); + afterSpell.collaterals["UNIV2AAVEETH-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 300, + mat: 40000, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 3 * MILLION, + clip_buf: 13000, + clip_tail: 200 minutes, + clip_cusp: 4000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 130, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["UNIV2DAIUSDT-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 10 * THOUSAND, + pct: 200, + mat: 12500, + liqType: "clip", + liqOn: true, + chop: 1300, + dog_hole: 5 * MILLION, + clip_buf: 10500, + clip_tail: 220 minutes, + clip_cusp: 9000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 9500, + calc_tau: 0, + calc_step: 120, + calc_cut: 9990, + offboarding: false + }); + afterSpell.collaterals["RWA001-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 15 * MILLION, + dust: 0, + pct: 900, + mat: 10000, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA002-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 50 * MILLION, + dust: 0, + pct: 7_00, + mat: 100_00, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA003-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0 * MILLION, + aL_gap: 0 * MILLION, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 600, + mat: 10500, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA004-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0 * MILLION, + aL_gap: 0 * MILLION, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 700, + mat: 11000, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA005-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0 * MILLION, + aL_gap: 0 * MILLION, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 450, + mat: 10500, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA006-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0 * MILLION, + aL_gap: 0 * MILLION, + aL_ttl: 0, + line: 0 * MILLION, + dust: 0, + pct: 200, + mat: 10000, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA007-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 0, + mat: 10000, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA008-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 5, + mat: 10000, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA009-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 100_000_000, + dust: 0, + pct: 0, + mat: 10000, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA010-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 4_00, + mat: 100_00, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA011-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 4_00, + mat: 100_00, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA012-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 80_000_000, + dust: 0, + pct: 4_00, + mat: 100_00, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA013-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 70_000_000, + dust: 0, + pct: 4_00, + mat: 100_00, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA014-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 0, + mat: 100_00, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RWA015-A"] = CollateralValues({ + aL_enabled: true, + aL_line: 3_000_000_000, + aL_gap: 50_000_000, + aL_ttl: 24 hours, + line: 0, + dust: 0, + pct: 0, + mat: 100_00, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["MATIC-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 300, + mat: 10000_00, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 3 * MILLION, + clip_buf: 120_00, + clip_tail: 140 minutes, + clip_cusp: 40_00, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["PSM-PAX-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 0, + mat: 10000, + liqType: "clip", + liqOn: false, + chop: 1300, + dog_hole: 0, + clip_buf: 10500, + clip_tail: 220 minutes, + clip_cusp: 9000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 0, + cm_tolerance: 9500, + calc_tau: 0, + calc_step: 120, + calc_cut: 9990, + offboarding: true + }); + afterSpell.collaterals["GUNIV3DAIUSDC1-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 2, + mat: 10200, + liqType: "clip", + liqOn: false, + chop: 1300, + dog_hole: 5 * MILLION, + clip_buf: 10500, + clip_tail: 220 minutes, + clip_cusp: 9000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 0, + cm_tolerance: 9500, + calc_tau: 0, + calc_step: 120, + calc_cut: 9990, + offboarding: false + }); + afterSpell.collaterals["WSTETH-A"] = CollateralValues({ + aL_enabled: true, + aL_line: 750 * MILLION, + aL_gap: 30 * MILLION, + aL_ttl: 12 hours, + line: 0, + dust: 7_500, + pct: 7_25, + mat: 150_00, + liqType: "clip", + liqOn: true, + chop: 1300, + dog_hole: 30 * MILLION, + clip_buf: 110_00, + clip_tail: 7_200, + clip_cusp: 45_00, + clip_chip: 10, + clip_tip: 250, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: false + }); + afterSpell.collaterals["WSTETH-B"] = CollateralValues({ + aL_enabled: true, + aL_line: 1 * BILLION, + aL_gap: 45 * MILLION, + aL_ttl: 12 hours, + line: 0, + dust: 3_500, + pct: 7_00, + mat: 175_00, + liqType: "clip", + liqOn: true, + chop: 1300, + dog_hole: 20 * MILLION, + clip_buf: 110_00, + clip_tail: 7_200, + clip_cusp: 45_00, + clip_chip: 10, + clip_tip: 250, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: false + }); + afterSpell.collaterals["DIRECT-SPK-AAVE-LIDO-USDS"] = CollateralValues({ + aL_enabled: true, + aL_line: 100 * MILLION, + aL_gap: 50 * MILLION, + aL_ttl: 24 hours, + line: 0, + dust: 0, + pct: 0, + mat: 10000, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["DIRECT-AAVEV2-DAI"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 0, + mat: 10000, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["DIRECT-COMPV2-DAI"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 0, + mat: 10000, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["PSM-GUSD-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 0, + pct: 0, + mat: 10000, + liqType: "clip", + liqOn: false, + chop: 1300, + dog_hole: 0, + clip_buf: 10500, + clip_tail: 220 minutes, + clip_cusp: 9000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 0, + cm_tolerance: 9500, + calc_tau: 0, + calc_step: 120, + calc_cut: 9990, + offboarding: false + }); + afterSpell.collaterals["GUNIV3DAIUSDC2-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 15 * THOUSAND, + pct: 6, + mat: 10200, + liqType: "clip", + liqOn: false, + chop: 1300, + dog_hole: 5 * MILLION, + clip_buf: 10500, + clip_tail: 220 minutes, + clip_cusp: 9000, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 0, + cm_tolerance: 9500, + calc_tau: 0, + calc_step: 120, + calc_cut: 9990, + offboarding: false + }); + afterSpell.collaterals["CRVV1ETHSTETH-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 25 * THOUSAND, + pct: 4_24, + mat: 10000_00, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 5 * MILLION, + clip_buf: 110_00, + clip_tail: 120 minutes, + clip_cusp: 45_00, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 5000, + calc_tau: 0, + calc_step: 90, + calc_cut: 9900, + offboarding: true + }); + afterSpell.collaterals["TELEPORT-FW-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 2_100_000, + dust: 0, + pct: 0, + mat: 0, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["RETH-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 7_500, + pct: 5_25, + mat: 10000_00, + liqType: "clip", + liqOn: true, + chop: 0, + dog_hole: 2 * MILLION, + clip_buf: 110_00, + clip_tail: 120 minutes, + clip_cusp: 45_00, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 1, + cm_tolerance: 50_00, + calc_tau: 0, + calc_step: 90, + calc_cut: 99_00, + offboarding: true + }); + afterSpell.collaterals["GNO-A"] = CollateralValues({ + aL_enabled: false, + aL_line: 0, + aL_gap: 0, + aL_ttl: 0, + line: 0, + dust: 100 * THOUSAND, + pct: 4_90, + mat: 350_00, + liqType: "clip", + liqOn: true, + chop: 13_00, + dog_hole: 2 * MILLION, + clip_buf: 120_00, + clip_tail: 140 minutes, + clip_cusp: 25_00, + clip_chip: 10, + clip_tip: 250, + clipper_mom: 1, + cm_tolerance: 50_00, + calc_tau: 0, + calc_step: 60, + calc_cut: 99_00, + offboarding: false + }); + afterSpell.collaterals["DIRECT-SPARK-DAI"] = CollateralValues({ + aL_enabled: true, + aL_line: 2500 * MILLION, + aL_gap: 40 * MILLION, + aL_ttl: 24 hours, + line: 0, + dust: 0, + pct: 0, + mat: 10000, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["DIRECT-SPARK-MORPHO-DAI"] = CollateralValues({ + aL_enabled: true, + aL_line: 1 * BILLION, + aL_gap: 100 * MILLION, + aL_ttl: 24 hours, + line: 0, + dust: 0, + pct: 0, + mat: 10000, + liqType: "", + liqOn: false, + chop: 0, + dog_hole: 0, + clip_buf: 0, + clip_tail: 0, + clip_cusp: 0, + clip_chip: 0, + clip_tip: 0, + clipper_mom: 0, + cm_tolerance: 0, + calc_tau: 0, + calc_step: 0, + calc_cut: 0, + offboarding: false + }); + afterSpell.collaterals["LSE-MKR-A"] = CollateralValues({ + aL_enabled: true, + aL_line: 20_000_000, + aL_gap: 5_000_000, + aL_ttl: 16 hours, + line: 0, + dust: 30_000, + pct: 12_00, + mat: 200_00, + liqType: "clip", + liqOn: true, + chop: 8_00, + dog_hole: 3 * MILLION, + clip_buf: 120_00, + clip_tail: 100 minutes, + clip_cusp: 40_00, + clip_chip: 10, + clip_tip: 300, + clipper_mom: 1, + cm_tolerance: 50_00, + calc_tau: 0, + calc_step: 60, + calc_cut: 99_00, + offboarding: false + }); + } +} diff --git a/archive/2024-10-17-DssSpell/test/rates.sol b/archive/2024-10-17-DssSpell/test/rates.sol new file mode 100644 index 000000000..3d20b1e22 --- /dev/null +++ b/archive/2024-10-17-DssSpell/test/rates.sol @@ -0,0 +1,472 @@ +// SPDX-FileCopyrightText: © 2020 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity 0.8.16; + +contract Rates { + + mapping (uint256 => uint256) public rates; + + constructor() { + rates[ 0] = 1000000000000000000000000000; + rates[ 1] = 1000000000003170820659990704; + rates[ 2] = 1000000000006341324285480111; + rates[ 5] = 1000000000015850933588756013; + rates[ 6] = 1000000000019020169709960675; + rates[ 10] = 1000000000031693947650284507; + rates[ 25] = 1000000000079175551708715274; + rates[ 50] = 1000000000158153903837946257; + rates[ 75] = 1000000000236936036262880196; + rates[ 100] = 1000000000315522921573372069; + rates[ 125] = 1000000000393915525145987602; + rates[ 150] = 1000000000472114805215157978; + rates[ 175] = 1000000000550121712943459312; + rates[ 200] = 1000000000627937192491029810; + rates[ 225] = 1000000000705562181084137268; + rates[ 250] = 1000000000782997609082909351; + rates[ 275] = 1000000000860244400048238898; + rates[ 300] = 1000000000937303470807876289; + rates[ 319] = 1000000000995743377573746041; + rates[ 325] = 1000000001014175731521720677; + rates[ 333] = 1000000001038735548426731741; + rates[ 344] = 1000000001072474267302354182; + rates[ 345] = 1000000001075539644270067964; + rates[ 349] = 1000000001087798189708544327; + rates[ 350] = 1000000001090862085746321732; + rates[ 358] = 1000000001115362602336059074; + rates[ 370] = 1000000001152077919467240095; + rates[ 374] = 1000000001164306917698440949; + rates[ 375] = 1000000001167363430498603315; + rates[ 394] = 1000000001225381266358479708; + rates[ 400] = 1000000001243680656318820312; + rates[ 408] = 1000000001268063427242299977; + rates[ 420] = 1000000001304602465690389263; + rates[ 424] = 1000000001316772794769098706; + rates[ 425] = 1000000001319814647332759691; + rates[ 450] = 1000000001395766281313196627; + rates[ 475] = 1000000001471536429740616381; + rates[ 490] = 1000000001516911765932351183; + rates[ 500] = 1000000001547125957863212448; + rates[ 525] = 1000000001622535724756171269; + rates[ 544] = 1000000001679727448331902751; + rates[ 550] = 1000000001697766583380253701; + rates[ 554] = 1000000001709786974743980088; + rates[ 555] = 1000000001712791360746325100; + rates[ 561] = 1000000001730811701469052906; + rates[ 569] = 1000000001754822903403114680; + rates[ 575] = 1000000001772819380639683201; + rates[ 579] = 1000000001784811360376128985; + rates[ 580] = 1000000001787808646832390371; + rates[ 586] = 1000000001805786418479434295; + rates[ 600] = 1000000001847694957439350562; + rates[ 616] = 1000000001895522707144698926; + rates[ 619] = 1000000001904482384730282575; + rates[ 625] = 1000000001922394148741344865; + rates[ 629] = 1000000001934329706253075715; + rates[ 630] = 1000000001937312893803622469; + rates[ 636] = 1000000001955206127822364746; + rates[ 640] = 1000000001967129343622160710; + rates[ 641] = 1000000001970109447195256751; + rates[ 643] = 1000000001976068814257775407; + rates[ 645] = 1000000001982027061559507021; + rates[ 649] = 1000000001993940198563273844; + rates[ 650] = 1000000001996917783620820123; + rates[ 665] = 1000000002041548040175924154; + rates[ 668] = 1000000002050466558600245373; + rates[ 670] = 1000000002056410844314321266; + rates[ 674] = 1000000002068296073857195778; + rates[ 675] = 1000000002071266685321207000; + rates[ 691] = 1000000002118758660201099744; + rates[ 700] = 1000000002145441671308778766; + rates[ 716] = 1000000002192822766493423465; + rates[ 718] = 1000000002198740428552847104; + rates[ 720] = 1000000002204656986467871801; + rates[ 724] = 1000000002216486791512316847; + rates[ 725] = 1000000002219443553326580536; + rates[ 750] = 1000000002293273137447730714; + rates[ 775] = 1000000002366931224128103346; + rates[ 800] = 1000000002440418608258400030; + rates[ 825] = 1000000002513736079215619839; + rates[ 850] = 1000000002586884420913935572; + rates[ 875] = 1000000002659864411854984565; + rates[ 900] = 1000000002732676825177582095; + rates[ 925] = 1000000002805322428706865331; + rates[ 950] = 1000000002877801985002875644; + rates[ 975] = 1000000002950116251408586949; + rates[ 1000] = 1000000003022265980097387650; + rates[ 1025] = 1000000003094251918120023627; + rates[ 1050] = 1000000003166074807451009595; + rates[ 1075] = 1000000003237735385034516037; + rates[ 1100] = 1000000003309234382829738808; + rates[ 1125] = 1000000003380572527855758393; + rates[ 1150] = 1000000003451750542235895695; + rates[ 1175] = 1000000003522769143241571114; + rates[ 1200] = 1000000003593629043335673582; + rates[ 1225] = 1000000003664330950215446102; + rates[ 1250] = 1000000003734875566854894261; + rates[ 1275] = 1000000003805263591546724039; + rates[ 1300] = 1000000003875495717943815211; + rates[ 1325] = 1000000003945572635100236468; + rates[ 1350] = 1000000004015495027511808328; + rates[ 1375] = 1000000004085263575156219812; + rates[ 1400] = 1000000004154878953532704765; + rates[ 1425] = 1000000004224341833701283597; + rates[ 1450] = 1000000004293652882321576158; + rates[ 1475] = 1000000004362812761691191350; + rates[ 1500] = 1000000004431822129783699001; + rates[ 1525] = 1000000004500681640286189459; + rates[ 1550] = 1000000004569391942636426248; + rates[ 1575] = 1000000004637953682059597074; + rates[ 1600] = 1000000004706367499604668374; + rates[ 1625] = 1000000004774634032180348552; + rates[ 1650] = 1000000004842753912590664903; + rates[ 1675] = 1000000004910727769570159235; + rates[ 1700] = 1000000004978556227818707070; + rates[ 1725] = 1000000005046239908035965222; + rates[ 1750] = 1000000005113779426955452540; + rates[ 1775] = 1000000005181175397378268462; + rates[ 1800] = 1000000005248428428206454010; + rates[ 1825] = 1000000005315539124475999751; + rates[ 1850] = 1000000005382508087389505206; + rates[ 1875] = 1000000005449335914348494113; + rates[ 1900] = 1000000005516023198985389892; + rates[ 1925] = 1000000005582570531195155575; + rates[ 1950] = 1000000005648978497166602432; + rates[ 1975] = 1000000005715247679413371444; + rates[ 2000] = 1000000005781378656804591712; + rates[ 2025] = 1000000005847372004595219844; + rates[ 2050] = 1000000005913228294456064283; + rates[ 2075] = 1000000005978948094503498507; + rates[ 2100] = 1000000006044531969328866955; + rates[ 2125] = 1000000006109980480027587488; + rates[ 2150] = 1000000006175294184227954125; + rates[ 2175] = 1000000006240473636119643770; + rates[ 2200] = 1000000006305519386481930552; + rates[ 2225] = 1000000006370431982711611382; + rates[ 2250] = 1000000006435211968850646270; + rates[ 2275] = 1000000006499859885613516871; + rates[ 2300] = 1000000006564376270414306730; + rates[ 2325] = 1000000006628761657393506584; + rates[ 2350] = 1000000006693016577444548094; + rates[ 2375] = 1000000006757141558240069277; + rates[ 2400] = 1000000006821137124257914908; + rates[ 2425] = 1000000006885003796806875073; + rates[ 2450] = 1000000006948742094052165050; + rates[ 2475] = 1000000007012352531040649627; + rates[ 2500] = 1000000007075835619725814915; + rates[ 2525] = 1000000007139191868992490695; + rates[ 2550] = 1000000007202421784681326287; + rates[ 2575] = 1000000007265525869613022867; + rates[ 2600] = 1000000007328504623612325153; + rates[ 2625] = 1000000007391358543531775311; + rates[ 2650] = 1000000007454088123275231904; + rates[ 2675] = 1000000007516693853821156670; + rates[ 2700] = 1000000007579176223245671878; + rates[ 2725] = 1000000007641535716745390957; + rates[ 2750] = 1000000007703772816660025079; + rates[ 2775] = 1000000007765888002494768329; + rates[ 2800] = 1000000007827881750942464045; + rates[ 2825] = 1000000007889754535905554913; + rates[ 2850] = 1000000007951506828517819323; + rates[ 2875] = 1000000008013139097165896490; + rates[ 2900] = 1000000008074651807510602798; + rates[ 2925] = 1000000008136045422508041783; + rates[ 2950] = 1000000008197320402430510158; + rates[ 2975] = 1000000008258477204887202245; + rates[ 3000] = 1000000008319516284844715115; + rates[ 3025] = 1000000008380438094647356774; + rates[ 3050] = 1000000008441243084037259619; + rates[ 3075] = 1000000008501931700174301437; + rates[ 3100] = 1000000008562504387655836125; + rates[ 3125] = 1000000008622961588536236324; + rates[ 3150] = 1000000008683303742346250114; + rates[ 3175] = 1000000008743531286112173869; + rates[ 3200] = 1000000008803644654374843395; + rates[ 3225] = 1000000008863644279208445392; + rates[ 3250] = 1000000008923530590239151272; + rates[ 3275] = 1000000008983304014663575373; + rates[ 3300] = 1000000009042964977267059505; + rates[ 3325] = 1000000009102513900441785827; + rates[ 3350] = 1000000009161951204204719966; + rates[ 3375] = 1000000009221277306215386279; + rates[ 3400] = 1000000009280492621793477151; + rates[ 3425] = 1000000009339597563936298181; + rates[ 3450] = 1000000009398592543336051086; + rates[ 3475] = 1000000009457477968396956129; + rates[ 3500] = 1000000009516254245252215861; + rates[ 3525] = 1000000009574921777780821942; + rates[ 3550] = 1000000009633480967624206760; + rates[ 3575] = 1000000009691932214202741592; + rates[ 3600] = 1000000009750275914732082986; + rates[ 3625] = 1000000009808512464239369028; + rates[ 3650] = 1000000009866642255579267166; + rates[ 3675] = 1000000009924665679449875210; + rates[ 3700] = 1000000009982583124408477109; + rates[ 3725] = 1000000010040394976887155106; + rates[ 3750] = 1000000010098101621208259840; + rates[ 3775] = 1000000010155703439599739931; + rates[ 3800] = 1000000010213200812210332586; + rates[ 3825] = 1000000010270594117124616733; + rates[ 3850] = 1000000010327883730377930177; + rates[ 3875] = 1000000010385070025971152244; + rates[ 3900] = 1000000010442153375885353361; + rates[ 3925] = 1000000010499134150096313024; + rates[ 3950] = 1000000010556012716588907553; + rates[ 3975] = 1000000010612789441371369043; + rates[ 4000] = 1000000010669464688489416886; + rates[ 4025] = 1000000010726038820040263233; + rates[ 4050] = 1000000010782512196186493739; + rates[ 4075] = 1000000010838885175169824929; + rates[ 4100] = 1000000010895158113324739488; + rates[ 4125] = 1000000010951331365092000772; + rates[ 4150] = 1000000011007405283032047846; + rates[ 4175] = 1000000011063380217838272275; + rates[ 4200] = 1000000011119256518350177948; + rates[ 4225] = 1000000011175034531566425160; + rates[ 4250] = 1000000011230714602657760176; + rates[ 4275] = 1000000011286297074979831462; + rates[ 4300] = 1000000011341782290085893805; + rates[ 4325] = 1000000011397170587739401474; + rates[ 4350] = 1000000011452462305926491579; + rates[ 4375] = 1000000011507657780868358802; + rates[ 4400] = 1000000011562757347033522598; + rates[ 4425] = 1000000011617761337149988016; + rates[ 4450] = 1000000011672670082217301219; + rates[ 4475] = 1000000011727483911518500818; + rates[ 4500] = 1000000011782203152631966084; + rates[ 4525] = 1000000011836828131443163102; + rates[ 4550] = 1000000011891359172156289942; + rates[ 4575] = 1000000011945796597305821848; + rates[ 4600] = 1000000012000140727767957524; + rates[ 4625] = 1000000012054391882771967477; + rates[ 4650] = 1000000012108550379911445472; + rates[ 4675] = 1000000012162616535155464050; + rates[ 4700] = 1000000012216590662859635112; + rates[ 4725] = 1000000012270473075777076530; + rates[ 4750] = 1000000012324264085069285747; + rates[ 4775] = 1000000012377964000316921287; + rates[ 4800] = 1000000012431573129530493155; + rates[ 4825] = 1000000012485091779160962996; + rates[ 4850] = 1000000012538520254110254976; + rates[ 4875] = 1000000012591858857741678240; + rates[ 4900] = 1000000012645107891890261872; + rates[ 4925] = 1000000012698267656873003228; + rates[ 4950] = 1000000012751338451499030498; + rates[ 4975] = 1000000012804320573079680371; + rates[ 5000] = 1000000012857214317438491659; + rates[ 5025] = 1000000012910019978921115695; + rates[ 5050] = 1000000012962737850405144363; + rates[ 5075] = 1000000013015368223309856554; + rates[ 5100] = 1000000013067911387605883890; + rates[ 5125] = 1000000013120367631824796485; + rates[ 5150] = 1000000013172737243068609553; + rates[ 5175] = 1000000013225020507019211652; + rates[ 5200] = 1000000013277217707947715318; + rates[ 5225] = 1000000013329329128723730871; + rates[ 5250] = 1000000013381355050824564143; + rates[ 5275] = 1000000013433295754344338876; + rates[ 5300] = 1000000013485151518003044532; + rates[ 5325] = 1000000013536922619155510237; + rates[ 5350] = 1000000013588609333800305597; + rates[ 5375] = 1000000013640211936588569081; + rates[ 5400] = 1000000013691730700832764691; + rates[ 5425] = 1000000013743165898515367617; + rates[ 5450] = 1000000013794517800297479554; + rates[ 5475] = 1000000013845786675527374380; + rates[ 5500] = 1000000013896972792248974855; + rates[ 5525] = 1000000013948076417210261020; + rates[ 5550] = 1000000013999097815871610946; + rates[ 5575] = 1000000014050037252414074493; + rates[ 5600] = 1000000014100894989747580713; + rates[ 5625] = 1000000014151671289519079548; + rates[ 5650] = 1000000014202366412120618444; + rates[ 5675] = 1000000014252980616697354502; + rates[ 5700] = 1000000014303514161155502800; + rates[ 5725] = 1000000014353967302170221464; + rates[ 5750] = 1000000014404340295193434124; + rates[ 5775] = 1000000014454633394461590334; + rates[ 5800] = 1000000014504846853003364537; + rates[ 5825] = 1000000014554980922647294184; + rates[ 5850] = 1000000014605035854029357558; + rates[ 5875] = 1000000014655011896600491882; + rates[ 5900] = 1000000014704909298634052283; + rates[ 5925] = 1000000014754728307233212158; + rates[ 5950] = 1000000014804469168338305494; + rates[ 5975] = 1000000014854132126734111701; + rates[ 6000] = 1000000014903717426057083481; + rates[ 6025] = 1000000014953225308802518272; + rates[ 6050] = 1000000015002656016331673799; + rates[ 6075] = 1000000015052009788878828253; + rates[ 6100] = 1000000015101286865558285606; + rates[ 6125] = 1000000015150487484371326590; + rates[ 6150] = 1000000015199611882213105818; + rates[ 6175] = 1000000015248660294879495575; + rates[ 6200] = 1000000015297632957073876761; + rates[ 6225] = 1000000015346530102413877471; + rates[ 6250] = 1000000015395351963438059699; + rates[ 6275] = 1000000015444098771612554646; + rates[ 6300] = 1000000015492770757337647112; + rates[ 6325] = 1000000015541368149954309419; + rates[ 6350] = 1000000015589891177750685357; + rates[ 6375] = 1000000015638340067968524580; + rates[ 6400] = 1000000015686715046809567945; + rates[ 6425] = 1000000015735016339441884188; + rates[ 6450] = 1000000015783244170006158447; + rates[ 6475] = 1000000015831398761621933006; + rates[ 6500] = 1000000015879480336393800741; + rates[ 6525] = 1000000015927489115417551681; + rates[ 6550] = 1000000015975425318786273105; + rates[ 6575] = 1000000016023289165596403599; + rates[ 6600] = 1000000016071080873953741499; + rates[ 6625] = 1000000016118800660979408115; + rates[ 6650] = 1000000016166448742815766155; + rates[ 6675] = 1000000016214025334632293755; + rates[ 6700] = 1000000016261530650631414500; + rates[ 6725] = 1000000016308964904054283846; + rates[ 6750] = 1000000016356328307186532328; + rates[ 6775] = 1000000016403621071363965932; + rates[ 6800] = 1000000016450843406978224029; + rates[ 6825] = 1000000016497995523482395247; + rates[ 6850] = 1000000016545077629396591637; + rates[ 6875] = 1000000016592089932313481533; + rates[ 6900] = 1000000016639032638903781446; + rates[ 6925] = 1000000016685905954921707380; + rates[ 6950] = 1000000016732710085210385903; + rates[ 6975] = 1000000016779445233707225354; + rates[ 7000] = 1000000016826111603449247521; + rates[ 7025] = 1000000016872709396578380147; + rates[ 7050] = 1000000016919238814346710603; + rates[ 7075] = 1000000016965700057121701072; + rates[ 7100] = 1000000017012093324391365593; + rates[ 7125] = 1000000017058418814769409273; + rates[ 7150] = 1000000017104676726000330021; + rates[ 7175] = 1000000017150867254964483131; + rates[ 7200] = 1000000017196990597683109018; + rates[ 7225] = 1000000017243046949323324453; + rates[ 7250] = 1000000017289036504203077600; + rates[ 7275] = 1000000017334959455796067168; + rates[ 7300] = 1000000017380815996736626004; + rates[ 7325] = 1000000017426606318824569415; + rates[ 7350] = 1000000017472330613030008543; + rates[ 7375] = 1000000017517989069498129080; + rates[ 7400] = 1000000017563581877553935633; + rates[ 7425] = 1000000017609109225706962029; + rates[ 7450] = 1000000017654571301655947851; + rates[ 7475] = 1000000017699968292293481503; + rates[ 7500] = 1000000017745300383710610088; + rates[ 7525] = 1000000017790567761201416374; + rates[ 7550] = 1000000017835770609267563142; + rates[ 7575] = 1000000017880909111622805195; + rates[ 7600] = 1000000017925983451197469286; + rates[ 7625] = 1000000017970993810142902264; + rates[ 7650] = 1000000018015940369835887686; + rates[ 7675] = 1000000018060823310883031179; + rates[ 7700] = 1000000018105642813125114801; + rates[ 7725] = 1000000018150399055641420686; + rates[ 7750] = 1000000018195092216754024201; + rates[ 7775] = 1000000018239722474032056911; + rates[ 7800] = 1000000018284290004295939569; + rates[ 7825] = 1000000018328794983621585414; + rates[ 7850] = 1000000018373237587344574003; + rates[ 7875] = 1000000018417617990064295840; + rates[ 7900] = 1000000018461936365648068049; + rates[ 7925] = 1000000018506192887235221305; + rates[ 7950] = 1000000018550387727241158310; + rates[ 7975] = 1000000018594521057361384012; + rates[ 8000] = 1000000018638593048575507813; + rates[ 8025] = 1000000018682603871151218019; + rates[ 8050] = 1000000018726553694648228732; + rates[ 8075] = 1000000018770442687922199432; + rates[ 8100] = 1000000018814271019128627481; + rates[ 8125] = 1000000018858038855726713746; + rates[ 8150] = 1000000018901746364483201594; + rates[ 8175] = 1000000018945393711476189463; + rates[ 8200] = 1000000018988981062098917230; + rates[ 8225] = 1000000019032508581063526585; + rates[ 8250] = 1000000019075976432404795643; + rates[ 8275] = 1000000019119384779483847985; + rates[ 8300] = 1000000019162733784991836346; + rates[ 8325] = 1000000019206023610953601168; + rates[ 8350] = 1000000019249254418731304205; + rates[ 8375] = 1000000019292426369028037391; + rates[ 8400] = 1000000019335539621891407188; + rates[ 8425] = 1000000019378594336717094581; + rates[ 8450] = 1000000019421590672252390959; + rates[ 8475] = 1000000019464528786599710033; + rates[ 8500] = 1000000019507408837220076029; + rates[ 8525] = 1000000019550230980936588320; + rates[ 8550] = 1000000019592995373937862689; + rates[ 8575] = 1000000019635702171781449432; + rates[ 8600] = 1000000019678351529397228463; + rates[ 8625] = 1000000019720943601090781625; + rates[ 8650] = 1000000019763478540546742376; + rates[ 8675] = 1000000019805956500832123050; + rates[ 8700] = 1000000019848377634399619849; + rates[ 8725] = 1000000019890742093090895767; + rates[ 8750] = 1000000019933050028139841613; + rates[ 8775] = 1000000019975301590175815296; + rates[ 8800] = 1000000020017496929226859581; + rates[ 8825] = 1000000020059636194722898437; + rates[ 8850] = 1000000020101719535498912200; + rates[ 8875] = 1000000020143747099798091677; + rates[ 8900] = 1000000020185719035274971385; + rates[ 8925] = 1000000020227635488998542076; + rates[ 8950] = 1000000020269496607455342719; + rates[ 8975] = 1000000020311302536552532106; + rates[ 9000] = 1000000020353053421620940223; + rates[ 9025] = 1000000020394749407418099573; + rates[ 9050] = 1000000020436390638131256590; + rates[ 9075] = 1000000020477977257380363298; + rates[ 9100] = 1000000020519509408221049399; + rates[ 9125] = 1000000020560987233147574896; + rates[ 9150] = 1000000020602410874095763456; + rates[ 9175] = 1000000020643780472445916617; + rates[ 9200] = 1000000020685096169025709028; + rates[ 9225] = 1000000020726358104113064837; + rates[ 9250] = 1000000020767566417439015395; + rates[ 9275] = 1000000020808721248190538424; + rates[ 9300] = 1000000020849822735013378765; + rates[ 9325] = 1000000020890871016014850891; + rates[ 9350] = 1000000020931866228766623286; + rates[ 9375] = 1000000020972808510307484860; + rates[ 9400] = 1000000021013697997146093523; + rates[ 9425] = 1000000021054534825263707061; + rates[ 9450] = 1000000021095319130116896449; + rates[ 9475] = 1000000021136051046640241741; + rates[ 9500] = 1000000021176730709249010667; + rates[ 9525] = 1000000021217358251841820063; + rates[ 9550] = 1000000021257933807803280285; + rates[ 9575] = 1000000021298457510006622716; + rates[ 9600] = 1000000021338929490816310513; + rates[ 9625] = 1000000021379349882090632705; + rates[ 9650] = 1000000021419718815184281790; + rates[ 9675] = 1000000021460036420950914938; + rates[ 9700] = 1000000021500302829745698932; + rates[ 9725] = 1000000021540518171427838973; + rates[ 9750] = 1000000021580682575363091474; + rates[ 9775] = 1000000021620796170426260951; + rates[ 9800] = 1000000021660859085003681151; + rates[ 9825] = 1000000021700871446995680519; + rates[ 9850] = 1000000021740833383819032127; + rates[ 9875] = 1000000021780745022409388199; + rates[ 9900] = 1000000021820606489223699321; + rates[ 9925] = 1000000021860417910242618463; + rates[ 9950] = 1000000021900179410972889943; + rates[ 9975] = 1000000021939891116449723415; + rates[10000] = 1000000021979553151239153027; + } + +} diff --git a/archive/2024-10-17-DssSpell/test/starknet.t.sol b/archive/2024-10-17-DssSpell/test/starknet.t.sol new file mode 100644 index 000000000..d35faf420 --- /dev/null +++ b/archive/2024-10-17-DssSpell/test/starknet.t.sol @@ -0,0 +1,242 @@ +// SPDX-FileCopyrightText: © 2022 Dai Foundation +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity 0.8.16; + +import "../DssSpell.t.base.sol"; + +contract ConfigStarknet { + StarknetValues starknetValues; + + struct StarknetValues { + bytes32 l2_spell; + address core_implementation; + uint256 dai_bridge_isOpen; + uint256 dai_bridge_ceiling; + uint256 dai_bridge_maxDeposit; + uint256 l2_dai_bridge; + uint256 l2_gov_relay; + uint256 relay_selector; + } + + function setStarknetValues() public { + uint256 WAD = 10 ** 18; + + starknetValues = StarknetValues({ + l2_spell: 0, // Set to zero if no spell is set. + core_implementation: 0x47103A9b801eB6a63555897d399e4b7c1c8Eb5bC, // As of 2024-08-21 + dai_bridge_isOpen: 0, // 1 open, 0 closed + dai_bridge_ceiling: 5_000_000 * WAD, // wei + dai_bridge_maxDeposit: type(uint256).max, // wei + l2_dai_bridge: 0x075ac198e734e289a6892baa8dd14b21095f13bf8401900f5349d5569c3f6e60, + l2_gov_relay: 0x05f4d9b039f82e9a90125fb119ace0531f4936ff2a9a54a8598d49a4cd4bd6db, + relay_selector: 300224956480472355485152391090755024345070441743081995053718200325371913697 // Hardcoded in L1 gov relay, not public + }); + } +} + +interface StarknetEscrowMomLike { + function owner() external returns (address); + function authority() external returns (address); + function escrow() external returns (address); + function token() external returns (address); +} + +interface StarknetEscrowLike { + function wards(address) external returns(uint256); +} + +interface StarknetDaiBridgeLike { + function wards(address) external returns(uint256); + function isOpen() external returns (uint256); + function ceiling() external returns (uint256); + function maxDeposit() external returns (uint256); + function dai() external returns (address); + function starkNet() external returns (address); + function escrow() external returns (address); + function l2DaiBridge() external returns (uint256); +} + +interface StarknetGovRelayLike { + function wards(address) external returns (uint256); + function starkNet() external returns (address); + function l2GovernanceRelay() external returns (uint256); +} + +interface StarknetCoreLike { + function implementation() external returns (address); + function isNotFinalized() external returns (bool); + function l1ToL2Messages(bytes32) external returns (uint256); + function l1ToL2MessageNonce() external returns (uint256); +} + +interface DaiLike { + function allowance(address, address) external view returns (uint256); +} + +contract StarknetTests is DssSpellTestBase, ConfigStarknet { + + event LogMessageToL2( + address indexed fromAddress, + uint256 indexed toAddress, + uint256 indexed selector, + uint256[] payload, + uint256 nonce, + uint256 fee + ); + + constructor() { + setStarknetValues(); + } + + function testStarknet() public { + + _vote(address(spell)); + _scheduleWaitAndCast(address(spell)); + assertTrue(spell.done()); + + _checkStarknetEscrowMom(); + _checkStarknetEscrow(); + _checkStarknetDaiBridge(); + _checkStarknetGovRelay(); + _checkStarknetCore(); + } + + function testStarknetSpell() public { + + if (starknetValues.l2_spell != bytes32(0)) { + // Ensure the Pause Proxy has some ETH for the Starknet Spell + assertGt(pauseProxy.balance, 0); + _vote(address(spell)); + DssSpell(spell).schedule(); + + vm.warp(DssSpell(spell).nextCastTime()); + + vm.expectEmit(true, true, true, false, addr.addr("STARKNET_CORE")); + emit LogMessageToL2(addr.addr("STARKNET_GOV_RELAY"), starknetValues.l2_gov_relay, starknetValues.relay_selector, _payload(starknetValues.l2_spell), 0, 0); + DssSpell(spell).cast(); + + assertTrue(spell.done()); + + _checkStarknetMessage(starknetValues.l2_spell); + } + } + + function _checkStarknetEscrowMom() internal { + StarknetEscrowMomLike escrowMom = StarknetEscrowMomLike(addr.addr("STARKNET_ESCROW_MOM")); + + assertEq(escrowMom.owner(), addr.addr("MCD_PAUSE_PROXY"), "StarknetTest/pause-proxy-not-owner-on-escrow-mom"); + assertEq(escrowMom.authority(), addr.addr("MCD_ADM"), "StarknetTest/chief-not-authority-on-escrow-mom"); + assertEq(escrowMom.escrow(), addr.addr("STARKNET_ESCROW"), "StarknetTest/unexpected-escrow-on-escrow-mom"); + assertEq(escrowMom.token(), addr.addr("MCD_DAI"), "StarknetTest/unexpected-dai-on-escrow-mom"); + } + + function _checkStarknetEscrow() internal { + StarknetEscrowLike escrow = StarknetEscrowLike(addr.addr("STARKNET_ESCROW")); + + assertEq(escrow.wards(addr.addr("MCD_PAUSE_PROXY")), 1, "StarknetTest/pause-proxy-not-ward-on-escrow"); + assertEq(escrow.wards(addr.addr("MCD_ESM")), 1, "StarknetTest/esm-not-ward-on-escrow"); + assertEq(escrow.wards(addr.addr("STARKNET_ESCROW_MOM")), 1, "StarknetTest/escrow-mom-not-ward-on-escrow"); + + DaiLike dai = DaiLike(addr.addr("MCD_DAI")); + + assertEq(dai.allowance(addr.addr("STARKNET_ESCROW"), addr.addr("STARKNET_DAI_BRIDGE")), type(uint256).max, "StarknetTest/unexpected-escrow-allowance"); + assertEq(dai.allowance(addr.addr("STARKNET_ESCROW"), addr.addr("STARKNET_DAI_BRIDGE_LEGACY")), 0, "StarknetTest/unexpected-legacy-escrow-allowance"); + } + + function _checkStarknetDaiBridge() internal { + StarknetDaiBridgeLike daiBridge = StarknetDaiBridgeLike(addr.addr("STARKNET_DAI_BRIDGE")); + + assertEq(daiBridge.isOpen(), starknetValues.dai_bridge_isOpen, "StarknetTestError/dai-bridge-isOpen-unexpected"); + assertEq(daiBridge.ceiling(), starknetValues.dai_bridge_ceiling, "StarknetTestError/dai-bridge-ceiling-unexpected"); + assertEq(daiBridge.maxDeposit(), starknetValues.dai_bridge_maxDeposit, "StarknetTestError/dai-bridge-maxDeposit-unexpected"); + + assertEq(daiBridge.dai(), addr.addr("MCD_DAI"), "StarknetTest/dai-bridge-dai"); + assertEq(daiBridge.starkNet(), addr.addr("STARKNET_CORE"), "StarknetTest/dai-bridge-core"); + assertEq(daiBridge.escrow(), addr.addr("STARKNET_ESCROW"), "StarknetTest/dai-bridge-escrow"); + + assertEq(daiBridge.wards(addr.addr("MCD_PAUSE_PROXY")), 1, "StarknetTest/pause-proxy-not-ward-on-dai-bridge"); + assertEq(daiBridge.wards(addr.addr("MCD_ESM")), 1, "StarknetTest/esm-not-ward-on-dai-bridge"); + + assertEq(daiBridge.l2DaiBridge(), starknetValues.l2_dai_bridge, "StarknetTest/wrong-l2-dai-bridge-on-dai-bridge"); + } + + function _checkStarknetGovRelay() internal { + StarknetGovRelayLike govRelay = StarknetGovRelayLike(addr.addr("STARKNET_GOV_RELAY")); + + assertEq(govRelay.wards(addr.addr("MCD_PAUSE_PROXY")), 1, "StarknetTest/pause-proxy-not-ward-on-gov-relay"); + assertEq(govRelay.wards(addr.addr("MCD_ESM")), 1, "StarknetTest/esm-not-ward-on-gov-relay"); + + assertEq(govRelay.starkNet(), addr.addr("STARKNET_CORE"), "StarknetTest/unexpected-starknet-core-on-gov-relay"); + assertEq(govRelay.l2GovernanceRelay(), starknetValues.l2_gov_relay, "StarknetTest/unexpected-l2-gov-relay-on-gov-relay"); + } + + function _checkStarknetCore() internal { + StarknetCoreLike core = StarknetCoreLike(addr.addr("STARKNET_CORE")); + + // Checks to see that starknet core implementation matches core + // If the core implementation changes, inspect the new implementation for safety and update the config + // Note: message checks may fail if the message structure changes in the new implementation + assertEq(core.implementation(), starknetValues.core_implementation, _concat("StarknetTest/new-core-implementation-", bytes32(uint256(uint160(core.implementation()))))); + + assertTrue(core.isNotFinalized()); + } + + + function _checkStarknetMessage(bytes32 _spell) internal { + StarknetCoreLike core = StarknetCoreLike(addr.addr("STARKNET_CORE")); + + if (_spell != 0) { + + // Nonce increments each message, back up one + uint256 _nonce = core.l1ToL2MessageNonce() - 1; + + // Hash of message created by Starknet Core + bytes32 _message = _getL1ToL2MsgHash(addr.addr("STARKNET_GOV_RELAY"), starknetValues.l2_gov_relay, starknetValues.relay_selector, _payload(_spell), _nonce); + + // Assert message is scheduled, core returns 0 if not in message array + assertTrue(core.l1ToL2Messages(_message) > 0, "StarknetTest/SpellNotQueued"); + } + } + + function _payload(bytes32 _spell) internal pure returns (uint256[] memory) { + // Payload must be array + uint256[] memory payload_ = new uint256[](1); + payload_[0] = uint256(_spell); + return payload_; + } + + // Modified version of internal getL1ToL2MsgHash in Starknet Core implementation + function _getL1ToL2MsgHash( + address sender, + uint256 toAddress, + uint256 selector, + uint256[] memory payload, + uint256 nonce + ) internal pure returns (bytes32) { + return + keccak256( + abi.encodePacked( + uint256(uint160(sender)), + toAddress, + nonce, + selector, + payload.length, + payload + ) + ); + } +}