diff --git a/foundry.toml b/foundry.toml index f2c73111..b261c819 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,7 +4,8 @@ out = 'out' libs = ['lib'] fs_permissions = [ { access = "read", path = "./lib/dss-test/script/input/"}, - { access = "read", path = "./out/ArbitrumDomain.sol/ArbSysOverride.json"} + { access = "read", path = "./out/ArbitrumDomain.sol/ArbSysOverride.json"}, + { access = "read-write", path = "./spellblock.txt"} ] [rpc_endpoints] diff --git a/spellblock.txt b/spellblock.txt new file mode 100644 index 00000000..18a042d9 --- /dev/null +++ b/spellblock.txt @@ -0,0 +1 @@ +8451434 \ No newline at end of file diff --git a/src/DssSpell.t.base.sol b/src/DssSpell.t.base.sol index a4827611..f01ac45c 100644 --- a/src/DssSpell.t.base.sol +++ b/src/DssSpell.t.base.sol @@ -266,24 +266,60 @@ contract DssSpellTestBase is Config, DssTest { function setUp() public { setValues(address(chief)); - spellValues.deployed_spell_created = spellValues.deployed_spell != address(0) ? spellValues.deployed_spell_created : block.timestamp; + if (spellValues.deployed_spell != address(0)) { + spell = DssSpell(spellValues.deployed_spell); + if (spell.eta() != 0) { + // if we have a scheduled 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(this)); + vm.makePersistent(address(rates)); + vm.makePersistent(address(addr)); + vm.makePersistent(address(deployers)); + _rollToDeployedSpellBlock(address(spell)); + } + } else { + spell = new DssSpell(); + } + _castPreviousSpell(); - 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(this)); - vm.makePersistent(address(rates)); - vm.makePersistent(address(addr)); - vm.makePersistent(address(deployers)); - vm.rollFork(spellValues.deployed_spell_block); + } + + function _rollToDeployedSpellBlock(address _spell) internal { + if (_spell == address(0)) { + vm.rollFork(block.number); + return; + } + + string memory spellBlockPath = "./spellblock.txt"; + + uint256 low = vm.parseUint(vm.readLine(spellBlockPath)); + uint256 high = block.number; + uint256 mid; + + vm.rollFork(low); + if (!_isContractDeployed(_spell)) { + while (low < high) { + mid = (low & high) + (low ^ high) / 2; // rounds down + vm.rollFork(mid); + if (_isContractDeployed(_spell)) { + high = mid; + } else { + low = mid + 1; + } + } + vm.writeFile(spellBlockPath, vm.toString(low)); + vm.rollFork(low); } } + function _isContractDeployed(address _spell) internal view returns (bool) { + uint256 size; + assembly { size := extcodesize(_spell) } + return size > 0; + } + function _vote(address spell_) internal { if (chief.hat() != spell_) { _giveTokens(address(gov), 999999999999 ether); @@ -1446,11 +1482,7 @@ contract DssSpellTestBase is Config, DssTest { 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"); - } + assertEq(spell.expiration(), block.timestamp + spellValues.expiration_threshold, "TestError/spell-expiration"); assertTrue(spell.officeHours() == spellValues.office_hours_enabled, "TestError/spell-office-hours"); diff --git a/src/test/config.sol b/src/test/config.sol index bb59222a..47eb1ad7 100644 --- a/src/test/config.sol +++ b/src/test/config.sol @@ -20,8 +20,6 @@ contract Config { struct SpellValues { address deployed_spell; - uint256 deployed_spell_created; - uint256 deployed_spell_block; address previous_spell; bool office_hours_enabled; uint256 expiration_threshold; @@ -96,8 +94,6 @@ contract Config { // spellValues = SpellValues({ deployed_spell: address(0x89625bb73bA7Dccde985F690CEa2659525AB5b15), // populate with deployed spell if deployed - deployed_spell_created: 1675789032, // use `./scripts/get-created-timestamp.sh ` - deployed_spell_block: 8451434, // populate with the block where the spell was deployed previous_spell: address(0), // supply if there is a need to test prior to its cast() function being called on-chain. office_hours_enabled: false, // true if officehours is expected to be enabled in the spell expiration_threshold: 30 days // Amount of time before spell expires