From 925973e436063b553c0af92bec1aacce8c1c35c8 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Thu, 2 Nov 2023 11:19:28 -0400 Subject: [PATCH 01/21] feat: add outline for sparklend pauser --- .../certora_debug_log.txt | 0 .../resource_errors.json | 3 + .../certora_debug_log.txt | 0 .../resource_errors.json | 3 + .certora_internal/latest | 1 + src/Counter.sol | 14 --- src/SparkLendPauser.sol | 105 ++++++++++++++++++ src/interfaces/ISparkLendPauser.sol | 51 +++++++++ 8 files changed, 163 insertions(+), 14 deletions(-) create mode 100644 .certora_internal/23_11_01_14_31_52_102/certora_debug_log.txt create mode 100644 .certora_internal/23_11_01_14_31_52_102/resource_errors.json create mode 100644 .certora_internal/23_11_02_10_41_06_164/certora_debug_log.txt create mode 100644 .certora_internal/23_11_02_10_41_06_164/resource_errors.json create mode 120000 .certora_internal/latest delete mode 100644 src/Counter.sol create mode 100644 src/SparkLendPauser.sol create mode 100644 src/interfaces/ISparkLendPauser.sol diff --git a/.certora_internal/23_11_01_14_31_52_102/certora_debug_log.txt b/.certora_internal/23_11_01_14_31_52_102/certora_debug_log.txt new file mode 100644 index 0000000..e69de29 diff --git a/.certora_internal/23_11_01_14_31_52_102/resource_errors.json b/.certora_internal/23_11_01_14_31_52_102/resource_errors.json new file mode 100644 index 0000000..d9bd792 --- /dev/null +++ b/.certora_internal/23_11_01_14_31_52_102/resource_errors.json @@ -0,0 +1,3 @@ +{ + "topics": [] +} \ No newline at end of file diff --git a/.certora_internal/23_11_02_10_41_06_164/certora_debug_log.txt b/.certora_internal/23_11_02_10_41_06_164/certora_debug_log.txt new file mode 100644 index 0000000..e69de29 diff --git a/.certora_internal/23_11_02_10_41_06_164/resource_errors.json b/.certora_internal/23_11_02_10_41_06_164/resource_errors.json new file mode 100644 index 0000000..d9bd792 --- /dev/null +++ b/.certora_internal/23_11_02_10_41_06_164/resource_errors.json @@ -0,0 +1,3 @@ +{ + "topics": [] +} \ No newline at end of file diff --git a/.certora_internal/latest b/.certora_internal/latest new file mode 120000 index 0000000..f68a670 --- /dev/null +++ b/.certora_internal/latest @@ -0,0 +1 @@ +23_11_02_10_41_06_164 \ No newline at end of file diff --git a/src/Counter.sol b/src/Counter.sol deleted file mode 100644 index 146c9e2..0000000 --- a/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.13; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/src/SparkLendPauser.sol b/src/SparkLendPauser.sol new file mode 100644 index 0000000..3679ef6 --- /dev/null +++ b/src/SparkLendPauser.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import { ISparkLendPauser } from "src/interfaces/ISparkLendPauser.sol"; + +interface ProxyLike { + function exec(address target, bytes calldata args) external payable returns (bytes memory out); +} + +interface AuthorityLike { + function canCall(address src, address dst, bytes4 sig) external view returns (bool); +} + +interface IPoolConfiguratorLike { + function setPoolPause(bool paused) external; +} + +contract SparkLendPauser is ISparkLendPauser { + + /**********************************************************************************************/ + /*** Declarations and Constructor ***/ + /**********************************************************************************************/ + + address public immutable sparklendPoolConfigurator; + + address public authority; + + bool public canPause; + + mapping (address => uint256) public override wards; + + constructor(address sparklendPoolConfigurator_, address authority_) { + sparklendPoolConfigurator = sparklendPoolConfigurator_; + authority = authority_; + wards[msg.sender] = 1; + canPause = true; + emit Rely(msg.sender); + } + + /**********************************************************************************************/ + /*** Modifiers ***/ + /**********************************************************************************************/ + + modifier onlyWards { + require(wards[msg.sender] == 1, "SparkLendPauser/not-authorized"); + _; + } + + modifier auth { + require(isAuthorized(msg.sender, msg.sig), "SparkLendPauser/not-authorized"); + _; + } + + /**********************************************************************************************/ + /*** Auth Functions ***/ + /**********************************************************************************************/ + + function deny(address usr) external override onlyWards { + wards[usr] = 0; + emit Deny(usr); + } + + function rely(address usr) external override onlyWards { + wards[usr] = 1; + emit Rely(usr); + } + + function setAuthority(address authority_) external onlyWards { + address oldAuthority = authority; + authority = authority_; + emit SetAuthority(oldAuthority, authority_); + } + + function resetPause() external onlyWards { + canPause = true; + } + + /**********************************************************************************************/ + /*** Chief Functions ***/ + /**********************************************************************************************/ + + function pause() external auth { + require( + AuthorityLike(authority).canCall(msg.sender, address(this), msg.sig), + "SparkLendPauser/not-authorized" + ); + IPoolConfiguratorLike(sparklendPoolConfigurator).setPoolPause(true); + } + + /**********************************************************************************************/ + /*** Helper Functions ***/ + /**********************************************************************************************/ + + function isAuthorized(address src, bytes4 sig) internal view returns (bool) { + if (src == address(this)) { + return true; + } else if (wards[src] == 1) { + return true; + } else if (authority == address(0)) { + return false; + } else { + return AuthorityLike(authority).canCall(src, address(this), sig); + } + } + +} diff --git a/src/interfaces/ISparkLendPauser.sol b/src/interfaces/ISparkLendPauser.sol new file mode 100644 index 0000000..0a62716 --- /dev/null +++ b/src/interfaces/ISparkLendPauser.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.0; + +interface ISparkLendPauser { + + /**********************************************************************************************/ + /*** Events ***/ + /**********************************************************************************************/ + + event SetOwner(address indexed oldOwner, address indexed newOwner); + event SetAuthority(address indexed oldAuthority, address indexed newAuthority); + + /** + * @dev Event emitted when a new admin is removed from the Conduit. + * @param usr The address of the user to remove. + */ + event Deny(address indexed usr); + + /** + * @dev Event emitted when a new admin is added to the Conduit. + * @param usr The address of the user to add. + */ + event Rely(address indexed usr); + + /**********************************************************************************************/ + /*** Storage Variables ***/ + /**********************************************************************************************/ + + /** + * @dev Returns a 0 or 1 depending on if the user has been added as an admin. + * @return relied The value of the user's admin status. + */ + function wards(address user) external view returns (uint256 relied); + + /**********************************************************************************************/ + /*** Administrative Functions ***/ + /**********************************************************************************************/ + + /** + * @dev Function to remove an addresses admin permissions. + * @param usr The address of the admin. + */ + function deny(address usr) external; + + /** + * @dev Function to give an address admin permissions. + * @param usr The address of the new admin. + */ + function rely(address usr) external; + +} From 5e833db127019f1a8dfad7cd8d341943cf6aa073 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Thu, 2 Nov 2023 11:20:08 -0400 Subject: [PATCH 02/21] fix: rm certora files --- .certora_internal/23_11_01_14_31_52_102/certora_debug_log.txt | 0 .certora_internal/23_11_01_14_31_52_102/resource_errors.json | 3 --- .certora_internal/23_11_02_10_41_06_164/certora_debug_log.txt | 0 .certora_internal/23_11_02_10_41_06_164/resource_errors.json | 3 --- .certora_internal/latest | 1 - 5 files changed, 7 deletions(-) delete mode 100644 .certora_internal/23_11_01_14_31_52_102/certora_debug_log.txt delete mode 100644 .certora_internal/23_11_01_14_31_52_102/resource_errors.json delete mode 100644 .certora_internal/23_11_02_10_41_06_164/certora_debug_log.txt delete mode 100644 .certora_internal/23_11_02_10_41_06_164/resource_errors.json delete mode 120000 .certora_internal/latest diff --git a/.certora_internal/23_11_01_14_31_52_102/certora_debug_log.txt b/.certora_internal/23_11_01_14_31_52_102/certora_debug_log.txt deleted file mode 100644 index e69de29..0000000 diff --git a/.certora_internal/23_11_01_14_31_52_102/resource_errors.json b/.certora_internal/23_11_01_14_31_52_102/resource_errors.json deleted file mode 100644 index d9bd792..0000000 --- a/.certora_internal/23_11_01_14_31_52_102/resource_errors.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "topics": [] -} \ No newline at end of file diff --git a/.certora_internal/23_11_02_10_41_06_164/certora_debug_log.txt b/.certora_internal/23_11_02_10_41_06_164/certora_debug_log.txt deleted file mode 100644 index e69de29..0000000 diff --git a/.certora_internal/23_11_02_10_41_06_164/resource_errors.json b/.certora_internal/23_11_02_10_41_06_164/resource_errors.json deleted file mode 100644 index d9bd792..0000000 --- a/.certora_internal/23_11_02_10_41_06_164/resource_errors.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "topics": [] -} \ No newline at end of file diff --git a/.certora_internal/latest b/.certora_internal/latest deleted file mode 120000 index f68a670..0000000 --- a/.certora_internal/latest +++ /dev/null @@ -1 +0,0 @@ -23_11_02_10_41_06_164 \ No newline at end of file From b989973926e2e6dcb425f6bf3fdbbc4997b3b8b3 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Thu, 2 Nov 2023 11:35:24 -0400 Subject: [PATCH 03/21] feat: add test coverage for wards ACL functions --- src/SparkLendPauser.sol | 11 ++-- test/Counter.t.sol | 25 --------- test/SparkLendPauser.base.t.sol | 93 +++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 30 deletions(-) delete mode 100644 test/Counter.t.sol create mode 100644 test/SparkLendPauser.base.t.sol diff --git a/src/SparkLendPauser.sol b/src/SparkLendPauser.sol index 3679ef6..2aa9a48 100644 --- a/src/SparkLendPauser.sol +++ b/src/SparkLendPauser.sol @@ -41,7 +41,7 @@ contract SparkLendPauser is ISparkLendPauser { /**********************************************************************************************/ modifier onlyWards { - require(wards[msg.sender] == 1, "SparkLendPauser/not-authorized"); + require(wards[msg.sender] == 1, "SparkLendPauser/not-ward"); _; } @@ -51,7 +51,7 @@ contract SparkLendPauser is ISparkLendPauser { } /**********************************************************************************************/ - /*** Auth Functions ***/ + /*** Wards Functions ***/ /**********************************************************************************************/ function deny(address usr) external override onlyWards { @@ -70,15 +70,16 @@ contract SparkLendPauser is ISparkLendPauser { emit SetAuthority(oldAuthority, authority_); } - function resetPause() external onlyWards { - canPause = true; + function setCanPause(bool canPause_) external onlyWards { + canPause = canPause_; } /**********************************************************************************************/ - /*** Chief Functions ***/ + /*** Auth Functions ***/ /**********************************************************************************************/ function pause() external auth { + require(canPause, "SparkLendPauser/pause-not-allowed"); require( AuthorityLike(authority).canCall(msg.sender, address(this), msg.sig), "SparkLendPauser/not-authorized" diff --git a/test/Counter.t.sol b/test/Counter.t.sol deleted file mode 100644 index c90feb7..0000000 --- a/test/Counter.t.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.13; - -import "forge-std/Test.sol"; - -import { Counter } from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -} diff --git a/test/SparkLendPauser.base.t.sol b/test/SparkLendPauser.base.t.sol new file mode 100644 index 0000000..8dd0df7 --- /dev/null +++ b/test/SparkLendPauser.base.t.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; + +import { SparkLendPauser } from "../src/SparkLendPauser.sol"; + +contract ConfiguratorMock { + function setPause(bool paused) external {} +} + +contract SparkLendPauserUnitTestBase is Test { + + address public authority; + address public configurator; + address public ward; + + SparkLendPauser public pauser; + + function setUp() public { + authority = makeAddr("authority"); + ward = makeAddr("ward"); + + configurator = address(new ConfiguratorMock()); + pauser = new SparkLendPauser(configurator, authority); + + pauser.rely(ward); + pauser.deny(address(this)); + } + + function test_deny_no_auth() public { + vm.expectRevert("SparkLendPauser/not-ward"); + pauser.deny(ward); + } + + function test_deny() public { + assertEq(pauser.wards(ward), 1); + + vm.prank(ward); + pauser.deny(ward); + + assertEq(pauser.wards(ward), 0); + } + + function test_rely_no_auth() public { + vm.expectRevert("SparkLendPauser/not-ward"); + pauser.rely(makeAddr("new ward")); + } + + function test_rely() public { + address newWard = makeAddr("new ward"); + assertEq(pauser.wards(newWard), 0); + + vm.prank(ward); + pauser.rely(newWard); + + assertEq(pauser.wards(ward), 1); + } + + function test_setAuthority_no_auth() public { + vm.expectRevert("SparkLendPauser/not-ward"); + pauser.setAuthority(makeAddr("new authority")); + } + + function test_setAuthority() public { + address newAuthority = makeAddr("new authority"); + assertEq(pauser.authority(), authority); + + vm.prank(ward); + pauser.setAuthority(newAuthority); + + assertEq(pauser.authority(), newAuthority); + } + + function test_resetPause_no_auth() public { + vm.expectRevert("SparkLendPauser/not-ward"); + pauser.setCanPause(false); + } + + function test_setCanPause() public { + assertEq(pauser.canPause(), true); + + vm.startPrank(ward); + pauser.setCanPause(false); + + assertEq(pauser.canPause(), false); + + pauser.setCanPause(true); + + assertEq(pauser.canPause(), true); + } + +} From 8afcb351be0f34f0b5dc317265b97428fb007bb8 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Fri, 3 Nov 2023 11:00:34 -0400 Subject: [PATCH 04/21] feat: add pool mock, use freeze instead of pause --- ...arkLendPauser.sol => SparkLendFreezer.sol} | 51 ++++---- ...rkLendPauser.sol => ISparkLendFreezer.sol} | 2 +- test/Mocks.sol | 40 +++++++ test/SparkLendPauser.base.t.sol | 110 ++++++++++++------ 4 files changed, 148 insertions(+), 55 deletions(-) rename src/{SparkLendPauser.sol => SparkLendFreezer.sol} (69%) rename src/interfaces/{ISparkLendPauser.sol => ISparkLendFreezer.sol} (98%) create mode 100644 test/Mocks.sol diff --git a/src/SparkLendPauser.sol b/src/SparkLendFreezer.sol similarity index 69% rename from src/SparkLendPauser.sol rename to src/SparkLendFreezer.sol index 2aa9a48..b547ce9 100644 --- a/src/SparkLendPauser.sol +++ b/src/SparkLendFreezer.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -import { ISparkLendPauser } from "src/interfaces/ISparkLendPauser.sol"; +import { ISparkLendFreezer } from "src/interfaces/ISparkLendFreezer.sol"; interface ProxyLike { function exec(address target, bytes calldata args) external payable returns (bytes memory out); @@ -10,29 +10,36 @@ interface AuthorityLike { function canCall(address src, address dst, bytes4 sig) external view returns (bool); } -interface IPoolConfiguratorLike { - function setPoolPause(bool paused) external; +interface PoolConfiguratorLike { + function setReserveFreeze(address asset, bool freeze) external; } -contract SparkLendPauser is ISparkLendPauser { +interface PoolLike { + function getReservesList() external view returns (address[] memory); +} + +contract SparkLendFreezer is ISparkLendFreezer { /**********************************************************************************************/ /*** Declarations and Constructor ***/ /**********************************************************************************************/ - address public immutable sparklendPoolConfigurator; + address public immutable poolConfigurator; + address public immutable pool; address public authority; - bool public canPause; + bool public canFreeze; mapping (address => uint256) public override wards; - constructor(address sparklendPoolConfigurator_, address authority_) { - sparklendPoolConfigurator = sparklendPoolConfigurator_; - authority = authority_; + constructor(address poolConfigurator_, address pool_, address authority_) { + poolConfigurator = poolConfigurator_; + pool = pool_; + authority = authority_; + wards[msg.sender] = 1; - canPause = true; + canFreeze = true; emit Rely(msg.sender); } @@ -41,12 +48,12 @@ contract SparkLendPauser is ISparkLendPauser { /**********************************************************************************************/ modifier onlyWards { - require(wards[msg.sender] == 1, "SparkLendPauser/not-ward"); + require(wards[msg.sender] == 1, "SparkLendFreezer/not-ward"); _; } modifier auth { - require(isAuthorized(msg.sender, msg.sig), "SparkLendPauser/not-authorized"); + require(isAuthorized(msg.sender, msg.sig), "SparkLendFreezer/not-authorized"); _; } @@ -70,21 +77,23 @@ contract SparkLendPauser is ISparkLendPauser { emit SetAuthority(oldAuthority, authority_); } - function setCanPause(bool canPause_) external onlyWards { - canPause = canPause_; + function setCanFreeze(bool canFreeze_) external onlyWards { + canFreeze = canFreeze_; } /**********************************************************************************************/ /*** Auth Functions ***/ /**********************************************************************************************/ - function pause() external auth { - require(canPause, "SparkLendPauser/pause-not-allowed"); - require( - AuthorityLike(authority).canCall(msg.sender, address(this), msg.sig), - "SparkLendPauser/not-authorized" - ); - IPoolConfiguratorLike(sparklendPoolConfigurator).setPoolPause(true); + function freeze() external auth { + require(canFreeze, "SparkLendFreezer/pause-not-allowed"); + + address[] memory reserves = PoolLike(pool).getReservesList(); + + for (uint256 i = 0; i < reserves.length; i++) { + if (reserves[i] == address(0)) continue; + PoolConfiguratorLike(poolConfigurator).setReserveFreeze(reserves[i], true); + } } /**********************************************************************************************/ diff --git a/src/interfaces/ISparkLendPauser.sol b/src/interfaces/ISparkLendFreezer.sol similarity index 98% rename from src/interfaces/ISparkLendPauser.sol rename to src/interfaces/ISparkLendFreezer.sol index 0a62716..ee692f8 100644 --- a/src/interfaces/ISparkLendPauser.sol +++ b/src/interfaces/ISparkLendFreezer.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity ^0.8.0; -interface ISparkLendPauser { +interface ISparkLendFreezer { /**********************************************************************************************/ /*** Events ***/ diff --git a/test/Mocks.sol b/test/Mocks.sol new file mode 100644 index 0000000..89cafad --- /dev/null +++ b/test/Mocks.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.13; + +contract AuthorityMock { + + bool internal _allCanCall; + + mapping (address => mapping (address => mapping (bytes4 => bool))) internal callAllowed; + + function __setCanCall(address src, address dst, bytes4 sig, bool allowed) public { + callAllowed[src][dst][sig] = allowed; + } + + function _setAllCanCall(bool allCanCall_) public { + _allCanCall = allCanCall_; + } + + function canCall(address src, address dst, bytes4 sig) external view returns (bool) { + return callAllowed[src][dst][sig] || _allCanCall; + } + +} + +contract ConfiguratorMock { + + function setReserveFreeze(address asset, bool freeze) external {} + +} + +contract PoolMock { + + function getReservesList() external pure returns (address[] memory list) { + list = new address[](1); + + list[0] = address(0x1); + } + +} + + diff --git a/test/SparkLendPauser.base.t.sol b/test/SparkLendPauser.base.t.sol index 8dd0df7..fa2ff62 100644 --- a/test/SparkLendPauser.base.t.sol +++ b/test/SparkLendPauser.base.t.sol @@ -3,91 +3,135 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; -import { SparkLendPauser } from "../src/SparkLendPauser.sol"; +import { SparkLendFreezer } from "../src/SparkLendFreezer.sol"; -contract ConfiguratorMock { - function setPause(bool paused) external {} -} +import { ConfiguratorMock, PoolMock } from "./Mocks.sol"; -contract SparkLendPauserUnitTestBase is Test { +contract SparkLendFreezerUnitTests is Test { address public authority; address public configurator; + address public pool; address public ward; - SparkLendPauser public pauser; + SparkLendFreezer public freezer; function setUp() public { authority = makeAddr("authority"); ward = makeAddr("ward"); configurator = address(new ConfiguratorMock()); - pauser = new SparkLendPauser(configurator, authority); + pool = address(new PoolMock()); + freezer = new SparkLendFreezer(configurator, pool, authority); - pauser.rely(ward); - pauser.deny(address(this)); + freezer.rely(ward); + freezer.deny(address(this)); } + /********************/ + /*** `deny` Tests ***/ + /********************/ + function test_deny_no_auth() public { - vm.expectRevert("SparkLendPauser/not-ward"); - pauser.deny(ward); + vm.expectRevert("SparkLendFreezer/not-ward"); + freezer.deny(ward); } function test_deny() public { - assertEq(pauser.wards(ward), 1); + assertEq(freezer.wards(ward), 1); vm.prank(ward); - pauser.deny(ward); + freezer.deny(ward); - assertEq(pauser.wards(ward), 0); + assertEq(freezer.wards(ward), 0); } + /********************/ + /*** `rely` Tests ***/ + /********************/ + function test_rely_no_auth() public { - vm.expectRevert("SparkLendPauser/not-ward"); - pauser.rely(makeAddr("new ward")); + vm.expectRevert("SparkLendFreezer/not-ward"); + freezer.rely(makeAddr("new ward")); } function test_rely() public { address newWard = makeAddr("new ward"); - assertEq(pauser.wards(newWard), 0); + assertEq(freezer.wards(newWard), 0); vm.prank(ward); - pauser.rely(newWard); + freezer.rely(newWard); - assertEq(pauser.wards(ward), 1); + assertEq(freezer.wards(ward), 1); } + /****************************/ + /*** `setAuthority` Tests ***/ + /****************************/ + function test_setAuthority_no_auth() public { - vm.expectRevert("SparkLendPauser/not-ward"); - pauser.setAuthority(makeAddr("new authority")); + vm.expectRevert("SparkLendFreezer/not-ward"); + freezer.setAuthority(makeAddr("new authority")); } function test_setAuthority() public { address newAuthority = makeAddr("new authority"); - assertEq(pauser.authority(), authority); + assertEq(freezer.authority(), authority); vm.prank(ward); - pauser.setAuthority(newAuthority); + freezer.setAuthority(newAuthority); - assertEq(pauser.authority(), newAuthority); + assertEq(freezer.authority(), newAuthority); } - function test_resetPause_no_auth() public { - vm.expectRevert("SparkLendPauser/not-ward"); - pauser.setCanPause(false); + /**************************/ + /*** `setCanFreeze` Tests ***/ + /**************************/ + + function test_setCanFreeze_no_auth() public { + vm.expectRevert("SparkLendFreezer/not-ward"); + freezer.setCanFreeze(false); } - function test_setCanPause() public { - assertEq(pauser.canPause(), true); + function test_setCanFreeze() public { + assertEq(freezer.canFreeze(), true); vm.startPrank(ward); - pauser.setCanPause(false); + freezer.setCanFreeze(false); - assertEq(pauser.canPause(), false); + assertEq(freezer.canFreeze(), false); - pauser.setCanPause(true); + freezer.setCanFreeze(true); - assertEq(pauser.canPause(), true); + assertEq(freezer.canFreeze(), true); } + // /*********************/ + // /*** `pause` Tests ***/ + // /*********************/ + + // function test_freeze_noAuth() public { + // assertEq(freezer.canPause(), true); + + // vm.startPrank(ward); + // freezer.setCanFreeze(false); + + // assertEq(freezer.canPause(), false); + + // freezer.setCanFreeze(true); + + // assertEq(freezer.canPause(), true); + // } + + // function test_pause_cannotPause() public { + // vm.startPrank(ward); + // freezer.setCanFreeze(false); + + // vm.expectRevert("SparkLendFreezer/pause-not-allowed"); + + // freezer.setCanFreeze(false); + // } + } + + From 5d54d716e6b2b2e49596b1d4c93be08f098fa37b Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Fri, 3 Nov 2023 11:20:09 -0400 Subject: [PATCH 05/21] feat: add test coverage for freeze --- src/SparkLendFreezer.sol | 25 +++++----- test/Mocks.sol | 14 ++++-- test/SparkLendPauser.base.t.sol | 81 +++++++++++++++++++++------------ 3 files changed, 76 insertions(+), 44 deletions(-) diff --git a/src/SparkLendFreezer.sol b/src/SparkLendFreezer.sol index b547ce9..1d50eec 100644 --- a/src/SparkLendFreezer.sol +++ b/src/SparkLendFreezer.sol @@ -47,13 +47,8 @@ contract SparkLendFreezer is ISparkLendFreezer { /*** Modifiers ***/ /**********************************************************************************************/ - modifier onlyWards { - require(wards[msg.sender] == 1, "SparkLendFreezer/not-ward"); - _; - } - modifier auth { - require(isAuthorized(msg.sender, msg.sig), "SparkLendFreezer/not-authorized"); + require(wards[msg.sender] == 1, "SparkLendFreezer/not-authorized"); _; } @@ -61,23 +56,23 @@ contract SparkLendFreezer is ISparkLendFreezer { /*** Wards Functions ***/ /**********************************************************************************************/ - function deny(address usr) external override onlyWards { + function deny(address usr) external override auth { wards[usr] = 0; emit Deny(usr); } - function rely(address usr) external override onlyWards { + function rely(address usr) external override auth { wards[usr] = 1; emit Rely(usr); } - function setAuthority(address authority_) external onlyWards { + function setAuthority(address authority_) external auth { address oldAuthority = authority; authority = authority_; emit SetAuthority(oldAuthority, authority_); } - function setCanFreeze(bool canFreeze_) external onlyWards { + function setCanFreeze(bool canFreeze_) external auth { canFreeze = canFreeze_; } @@ -85,8 +80,12 @@ contract SparkLendFreezer is ISparkLendFreezer { /*** Auth Functions ***/ /**********************************************************************************************/ - function freeze() external auth { - require(canFreeze, "SparkLendFreezer/pause-not-allowed"); + function freeze() external { + require(canFreeze, "SparkLendFreezer/freeze-not-allowed"); + require( + AuthorityLike(authority).canCall(msg.sender, address(this), msg.sig), + "SparkLendFreezer/cannot-call" + ); address[] memory reserves = PoolLike(pool).getReservesList(); @@ -94,6 +93,8 @@ contract SparkLendFreezer is ISparkLendFreezer { if (reserves[i] == address(0)) continue; PoolConfiguratorLike(poolConfigurator).setReserveFreeze(reserves[i], true); } + + canFreeze = false; } /**********************************************************************************************/ diff --git a/test/Mocks.sol b/test/Mocks.sol index 89cafad..6a40210 100644 --- a/test/Mocks.sol +++ b/test/Mocks.sol @@ -29,10 +29,18 @@ contract ConfiguratorMock { contract PoolMock { - function getReservesList() external pure returns (address[] memory list) { - list = new address[](1); + address[] internal assets; - list[0] = address(0x1); + function getReservesList() external view returns (address[] memory list) { + list = new address[](assets.length); + + for (uint256 i; i < assets.length; ++i) { + list[i] = assets[i]; + } + } + + function __addAsset(address asset) public { + assets.push(asset); } } diff --git a/test/SparkLendPauser.base.t.sol b/test/SparkLendPauser.base.t.sol index fa2ff62..ea91d89 100644 --- a/test/SparkLendPauser.base.t.sol +++ b/test/SparkLendPauser.base.t.sol @@ -5,24 +5,25 @@ import "forge-std/Test.sol"; import { SparkLendFreezer } from "../src/SparkLendFreezer.sol"; -import { ConfiguratorMock, PoolMock } from "./Mocks.sol"; +import { AuthorityMock, ConfiguratorMock, PoolMock } from "./Mocks.sol"; contract SparkLendFreezerUnitTests is Test { - address public authority; address public configurator; address public pool; address public ward; + AuthorityMock public authority; + SparkLendFreezer public freezer; function setUp() public { - authority = makeAddr("authority"); - ward = makeAddr("ward"); + ward = makeAddr("ward"); + authority = new AuthorityMock(); configurator = address(new ConfiguratorMock()); pool = address(new PoolMock()); - freezer = new SparkLendFreezer(configurator, pool, authority); + freezer = new SparkLendFreezer(configurator, pool, address(authority)); freezer.rely(ward); freezer.deny(address(this)); @@ -33,7 +34,7 @@ contract SparkLendFreezerUnitTests is Test { /********************/ function test_deny_no_auth() public { - vm.expectRevert("SparkLendFreezer/not-ward"); + vm.expectRevert("SparkLendFreezer/not-authorized"); freezer.deny(ward); } @@ -51,7 +52,7 @@ contract SparkLendFreezerUnitTests is Test { /********************/ function test_rely_no_auth() public { - vm.expectRevert("SparkLendFreezer/not-ward"); + vm.expectRevert("SparkLendFreezer/not-authorized"); freezer.rely(makeAddr("new ward")); } @@ -70,13 +71,13 @@ contract SparkLendFreezerUnitTests is Test { /****************************/ function test_setAuthority_no_auth() public { - vm.expectRevert("SparkLendFreezer/not-ward"); + vm.expectRevert("SparkLendFreezer/not-authorized"); freezer.setAuthority(makeAddr("new authority")); } function test_setAuthority() public { address newAuthority = makeAddr("new authority"); - assertEq(freezer.authority(), authority); + assertEq(freezer.authority(), address(authority)); vm.prank(ward); freezer.setAuthority(newAuthority); @@ -84,12 +85,12 @@ contract SparkLendFreezerUnitTests is Test { assertEq(freezer.authority(), newAuthority); } - /**************************/ + /****************************/ /*** `setCanFreeze` Tests ***/ - /**************************/ + /****************************/ function test_setCanFreeze_no_auth() public { - vm.expectRevert("SparkLendFreezer/not-ward"); + vm.expectRevert("SparkLendFreezer/not-authorized"); freezer.setCanFreeze(false); } @@ -106,31 +107,53 @@ contract SparkLendFreezerUnitTests is Test { assertEq(freezer.canFreeze(), true); } - // /*********************/ - // /*** `pause` Tests ***/ - // /*********************/ + /**********************/ + /*** `freeze` Tests ***/ + /**********************/ + + function test_freeze_notAllowed() public { + vm.prank(ward); + freezer.setCanFreeze(false); + + vm.expectRevert("SparkLendFreezer/freeze-not-allowed"); + freezer.freeze(); + } + + function test_freeze_noAuth() public { + vm.expectRevert("SparkLendFreezer/cannot-call"); + freezer.freeze(); + } + + function test_freeze_cannotCallTwice() public { + authority.__setCanCall(address(this), address(freezer), freezer.freeze.selector, true); + + freezer.freeze(); - // function test_freeze_noAuth() public { - // assertEq(freezer.canPause(), true); + vm.expectRevert("SparkLendFreezer/freeze-not-allowed"); + freezer.freeze(); + } - // vm.startPrank(ward); - // freezer.setCanFreeze(false); + function test_freeze() public { + authority.__setCanCall(address(this), address(freezer), freezer.freeze.selector, true); - // assertEq(freezer.canPause(), false); + address asset1 = makeAddr("asset1"); + address asset2 = makeAddr("asset2"); - // freezer.setCanFreeze(true); + PoolMock(pool).__addAsset(asset1); + PoolMock(pool).__addAsset(asset2); - // assertEq(freezer.canPause(), true); - // } + bytes4 poolSig = PoolMock.getReservesList.selector; + bytes4 configSig = ConfiguratorMock.setReserveFreeze.selector; - // function test_pause_cannotPause() public { - // vm.startPrank(ward); - // freezer.setCanFreeze(false); + assertEq(freezer.canFreeze(), true); - // vm.expectRevert("SparkLendFreezer/pause-not-allowed"); + vm.expectCall(pool, abi.encodePacked(poolSig)); + vm.expectCall(configurator, abi.encodePacked(configSig, abi.encode(asset1, true))); + vm.expectCall(configurator, abi.encodePacked(configSig, abi.encode(asset2, true))); + freezer.freeze(); - // freezer.setCanFreeze(false); - // } + assertEq(freezer.canFreeze(), false); + } } From 9b0c0d316b03c59634d16b668fe1b325df11cc48 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Fri, 3 Nov 2023 11:22:57 -0400 Subject: [PATCH 06/21] feat: get to 100% unit test coverage --- src/SparkLendFreezer.sol | 16 ---------------- test/SparkLendPauser.base.t.sol | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/SparkLendFreezer.sol b/src/SparkLendFreezer.sol index 1d50eec..0a927ac 100644 --- a/src/SparkLendFreezer.sol +++ b/src/SparkLendFreezer.sol @@ -97,20 +97,4 @@ contract SparkLendFreezer is ISparkLendFreezer { canFreeze = false; } - /**********************************************************************************************/ - /*** Helper Functions ***/ - /**********************************************************************************************/ - - function isAuthorized(address src, bytes4 sig) internal view returns (bool) { - if (src == address(this)) { - return true; - } else if (wards[src] == 1) { - return true; - } else if (authority == address(0)) { - return false; - } else { - return AuthorityLike(authority).canCall(src, address(this), sig); - } - } - } diff --git a/test/SparkLendPauser.base.t.sol b/test/SparkLendPauser.base.t.sol index ea91d89..dec4e24 100644 --- a/test/SparkLendPauser.base.t.sol +++ b/test/SparkLendPauser.base.t.sol @@ -29,6 +29,20 @@ contract SparkLendFreezerUnitTests is Test { freezer.deny(address(this)); } + /***************************/ + /*** `constructor` Tests ***/ + /***************************/ + + function test_constructor() public { + freezer = new SparkLendFreezer(configurator, pool, address(authority)); + + assertEq(freezer.poolConfigurator(), configurator); + assertEq(freezer.pool(), pool); + assertEq(freezer.authority(), address(authority)); + assertEq(freezer.canFreeze(), true); + assertEq(freezer.wards(address(this)), 1); + } + /********************/ /*** `deny` Tests ***/ /********************/ From 70516b12b784412cb2aad727f67648bb4b86648f Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Fri, 3 Nov 2023 11:23:29 -0400 Subject: [PATCH 07/21] fix: rm counter.s.sol --- script/Counter.s.sol | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 script/Counter.s.sol diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index 20d39cf..0000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.13; - -import { Script, console2 } from "forge-std/Script.sol"; - -contract CounterScript is Script { - function setUp() public {} - - function run() public { - vm.broadcast(); - } -} From 0c149774a0bd7cdc4433339adb2cb39e62ac5291 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Fri, 3 Nov 2023 11:28:31 -0400 Subject: [PATCH 08/21] fix: add pragma and remove unused interface --- src/SparkLendFreezer.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/SparkLendFreezer.sol b/src/SparkLendFreezer.sol index 0a927ac..bbcec18 100644 --- a/src/SparkLendFreezer.sol +++ b/src/SparkLendFreezer.sol @@ -1,11 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.13; import { ISparkLendFreezer } from "src/interfaces/ISparkLendFreezer.sol"; -interface ProxyLike { - function exec(address target, bytes calldata args) external payable returns (bytes memory out); -} - interface AuthorityLike { function canCall(address src, address dst, bytes4 sig) external view returns (bool); } From c8109d1b2894bf12e6819e6efe46283d90b27902 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Fri, 3 Nov 2023 12:27:14 -0400 Subject: [PATCH 09/21] fix: add double underscore --- test/Mocks.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Mocks.sol b/test/Mocks.sol index 6a40210..da6467b 100644 --- a/test/Mocks.sol +++ b/test/Mocks.sol @@ -11,7 +11,7 @@ contract AuthorityMock { callAllowed[src][dst][sig] = allowed; } - function _setAllCanCall(bool allCanCall_) public { + function __setAllCanCall(bool allCanCall_) public { _allCanCall = allCanCall_; } From 327fba74145481afeed19072cd1c1b9a5928a675 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Mon, 6 Nov 2023 14:05:08 -0500 Subject: [PATCH 10/21] feat: add reserve freezes, update test structure --- src/SparkLendFreezer.sol | 23 ++++++++---- test/SparkLendPauser.base.t.sol | 66 +++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/SparkLendFreezer.sol b/src/SparkLendFreezer.sol index bbcec18..1e7d282 100644 --- a/src/SparkLendFreezer.sol +++ b/src/SparkLendFreezer.sol @@ -49,6 +49,15 @@ contract SparkLendFreezer is ISparkLendFreezer { _; } + modifier freezeCheck { + require(canFreeze, "SparkLendFreezer/freeze-not-allowed"); + require( + AuthorityLike(authority).canCall(msg.sender, address(this), msg.sig), + "SparkLendFreezer/cannot-call" + ); + _; + } + /**********************************************************************************************/ /*** Wards Functions ***/ /**********************************************************************************************/ @@ -77,13 +86,7 @@ contract SparkLendFreezer is ISparkLendFreezer { /*** Auth Functions ***/ /**********************************************************************************************/ - function freeze() external { - require(canFreeze, "SparkLendFreezer/freeze-not-allowed"); - require( - AuthorityLike(authority).canCall(msg.sender, address(this), msg.sig), - "SparkLendFreezer/cannot-call" - ); - + function freezeAllMarkets() external freezeCheck { address[] memory reserves = PoolLike(pool).getReservesList(); for (uint256 i = 0; i < reserves.length; i++) { @@ -94,4 +97,10 @@ contract SparkLendFreezer is ISparkLendFreezer { canFreeze = false; } + function freezeMarket(address reserve) external freezeCheck { + PoolConfiguratorLike(poolConfigurator).setReserveFreeze(reserve, true); + + canFreeze = false; + } + } diff --git a/test/SparkLendPauser.base.t.sol b/test/SparkLendPauser.base.t.sol index dec4e24..5a323a3 100644 --- a/test/SparkLendPauser.base.t.sol +++ b/test/SparkLendPauser.base.t.sol @@ -7,7 +7,7 @@ import { SparkLendFreezer } from "../src/SparkLendFreezer.sol"; import { AuthorityMock, ConfiguratorMock, PoolMock } from "./Mocks.sol"; -contract SparkLendFreezerUnitTests is Test { +contract SparkLendFreezerUnitTestBase is Test { address public configurator; address public pool; @@ -29,9 +29,9 @@ contract SparkLendFreezerUnitTests is Test { freezer.deny(address(this)); } - /***************************/ - /*** `constructor` Tests ***/ - /***************************/ +} + +contract SparkLendFreezerConstructorTests is SparkLendFreezerUnitTestBase { function test_constructor() public { freezer = new SparkLendFreezer(configurator, pool, address(authority)); @@ -43,9 +43,9 @@ contract SparkLendFreezerUnitTests is Test { assertEq(freezer.wards(address(this)), 1); } - /********************/ - /*** `deny` Tests ***/ - /********************/ +} + +contract SparkLendFreezerDenyTests is SparkLendFreezerUnitTestBase { function test_deny_no_auth() public { vm.expectRevert("SparkLendFreezer/not-authorized"); @@ -61,9 +61,9 @@ contract SparkLendFreezerUnitTests is Test { assertEq(freezer.wards(ward), 0); } - /********************/ - /*** `rely` Tests ***/ - /********************/ +} + +contract SparkLendFreezerRelyTests is SparkLendFreezerUnitTestBase { function test_rely_no_auth() public { vm.expectRevert("SparkLendFreezer/not-authorized"); @@ -80,9 +80,9 @@ contract SparkLendFreezerUnitTests is Test { assertEq(freezer.wards(ward), 1); } - /****************************/ - /*** `setAuthority` Tests ***/ - /****************************/ +} + +contract SparkLendFreezerSetAuthorityTests is SparkLendFreezerUnitTestBase { function test_setAuthority_no_auth() public { vm.expectRevert("SparkLendFreezer/not-authorized"); @@ -99,6 +99,10 @@ contract SparkLendFreezerUnitTests is Test { assertEq(freezer.authority(), newAuthority); } +} + +contract SparkLendFreezerSetCanFreezeTests is SparkLendFreezerUnitTestBase { + /****************************/ /*** `setCanFreeze` Tests ***/ /****************************/ @@ -121,34 +125,48 @@ contract SparkLendFreezerUnitTests is Test { assertEq(freezer.canFreeze(), true); } +} + +contract SparkLendFreezerFreezeAllMarketsTests is SparkLendFreezerUnitTestBase { + /**********************/ /*** `freeze` Tests ***/ /**********************/ - function test_freeze_notAllowed() public { + function test_freezeAllMarkets_notAllowed() public { vm.prank(ward); freezer.setCanFreeze(false); vm.expectRevert("SparkLendFreezer/freeze-not-allowed"); - freezer.freeze(); + freezer.freezeAllMarkets(); } - function test_freeze_noAuth() public { + function test_freezeAllMarkets_noAuth() public { vm.expectRevert("SparkLendFreezer/cannot-call"); - freezer.freeze(); + freezer.freezeAllMarkets(); } - function test_freeze_cannotCallTwice() public { - authority.__setCanCall(address(this), address(freezer), freezer.freeze.selector, true); + function test_freezeAllMarkets_cannotCallTwice() public { + authority.__setCanCall( + address(this), + address(freezer), + freezer.freezeAllMarkets.selector, + true + ); - freezer.freeze(); + freezer.freezeAllMarkets(); vm.expectRevert("SparkLendFreezer/freeze-not-allowed"); - freezer.freeze(); + freezer.freezeAllMarkets(); } - function test_freeze() public { - authority.__setCanCall(address(this), address(freezer), freezer.freeze.selector, true); + function test_freezeAllMarkets() public { + authority.__setCanCall( + address(this), + address(freezer), + freezer.freezeAllMarkets.selector, + true + ); address asset1 = makeAddr("asset1"); address asset2 = makeAddr("asset2"); @@ -164,7 +182,7 @@ contract SparkLendFreezerUnitTests is Test { vm.expectCall(pool, abi.encodePacked(poolSig)); vm.expectCall(configurator, abi.encodePacked(configSig, abi.encode(asset1, true))); vm.expectCall(configurator, abi.encodePacked(configSig, abi.encode(asset2, true))); - freezer.freeze(); + freezer.freezeAllMarkets(); assertEq(freezer.canFreeze(), false); } From 566cb4514f55dfd2b1d7ec48f95a2a9703fff2f3 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Mon, 6 Nov 2023 14:17:23 -0500 Subject: [PATCH 11/21] feat: add freeze and freeze all coverage --- src/SparkLendFreezer.sol | 19 +--- ...user.base.t.sol => SparkLendFreezer.t.sol} | 90 ++++++------------- 2 files changed, 33 insertions(+), 76 deletions(-) rename test/{SparkLendPauser.base.t.sol => SparkLendFreezer.t.sol} (67%) diff --git a/src/SparkLendFreezer.sol b/src/SparkLendFreezer.sol index 1e7d282..14d4db2 100644 --- a/src/SparkLendFreezer.sol +++ b/src/SparkLendFreezer.sol @@ -26,8 +26,6 @@ contract SparkLendFreezer is ISparkLendFreezer { address public authority; - bool public canFreeze; - mapping (address => uint256) public override wards; constructor(address poolConfigurator_, address pool_, address authority_) { @@ -36,7 +34,6 @@ contract SparkLendFreezer is ISparkLendFreezer { authority = authority_; wards[msg.sender] = 1; - canFreeze = true; emit Rely(msg.sender); } @@ -49,8 +46,7 @@ contract SparkLendFreezer is ISparkLendFreezer { _; } - modifier freezeCheck { - require(canFreeze, "SparkLendFreezer/freeze-not-allowed"); + modifier canCall { require( AuthorityLike(authority).canCall(msg.sender, address(this), msg.sig), "SparkLendFreezer/cannot-call" @@ -78,29 +74,22 @@ contract SparkLendFreezer is ISparkLendFreezer { emit SetAuthority(oldAuthority, authority_); } - function setCanFreeze(bool canFreeze_) external auth { - canFreeze = canFreeze_; - } - /**********************************************************************************************/ /*** Auth Functions ***/ /**********************************************************************************************/ - function freezeAllMarkets() external freezeCheck { + function freezeAllMarkets() external canCall { address[] memory reserves = PoolLike(pool).getReservesList(); for (uint256 i = 0; i < reserves.length; i++) { if (reserves[i] == address(0)) continue; PoolConfiguratorLike(poolConfigurator).setReserveFreeze(reserves[i], true); } - - canFreeze = false; } - function freezeMarket(address reserve) external freezeCheck { + function freezeMarket(address reserve) external canCall { PoolConfiguratorLike(poolConfigurator).setReserveFreeze(reserve, true); - - canFreeze = false; } } + diff --git a/test/SparkLendPauser.base.t.sol b/test/SparkLendFreezer.t.sol similarity index 67% rename from test/SparkLendPauser.base.t.sol rename to test/SparkLendFreezer.t.sol index 5a323a3..4a27f7c 100644 --- a/test/SparkLendPauser.base.t.sol +++ b/test/SparkLendFreezer.t.sol @@ -31,7 +31,7 @@ contract SparkLendFreezerUnitTestBase is Test { } -contract SparkLendFreezerConstructorTests is SparkLendFreezerUnitTestBase { +contract ConstructorTests is SparkLendFreezerUnitTestBase { function test_constructor() public { freezer = new SparkLendFreezer(configurator, pool, address(authority)); @@ -39,13 +39,12 @@ contract SparkLendFreezerConstructorTests is SparkLendFreezerUnitTestBase { assertEq(freezer.poolConfigurator(), configurator); assertEq(freezer.pool(), pool); assertEq(freezer.authority(), address(authority)); - assertEq(freezer.canFreeze(), true); assertEq(freezer.wards(address(this)), 1); } } -contract SparkLendFreezerDenyTests is SparkLendFreezerUnitTestBase { +contract DenyTests is SparkLendFreezerUnitTestBase { function test_deny_no_auth() public { vm.expectRevert("SparkLendFreezer/not-authorized"); @@ -63,7 +62,7 @@ contract SparkLendFreezerDenyTests is SparkLendFreezerUnitTestBase { } -contract SparkLendFreezerRelyTests is SparkLendFreezerUnitTestBase { +contract RelyTests is SparkLendFreezerUnitTestBase { function test_rely_no_auth() public { vm.expectRevert("SparkLendFreezer/not-authorized"); @@ -82,7 +81,7 @@ contract SparkLendFreezerRelyTests is SparkLendFreezerUnitTestBase { } -contract SparkLendFreezerSetAuthorityTests is SparkLendFreezerUnitTestBase { +contract SetAuthorityTests is SparkLendFreezerUnitTestBase { function test_setAuthority_no_auth() public { vm.expectRevert("SparkLendFreezer/not-authorized"); @@ -101,65 +100,13 @@ contract SparkLendFreezerSetAuthorityTests is SparkLendFreezerUnitTestBase { } -contract SparkLendFreezerSetCanFreezeTests is SparkLendFreezerUnitTestBase { - - /****************************/ - /*** `setCanFreeze` Tests ***/ - /****************************/ - - function test_setCanFreeze_no_auth() public { - vm.expectRevert("SparkLendFreezer/not-authorized"); - freezer.setCanFreeze(false); - } - - function test_setCanFreeze() public { - assertEq(freezer.canFreeze(), true); - - vm.startPrank(ward); - freezer.setCanFreeze(false); - - assertEq(freezer.canFreeze(), false); - - freezer.setCanFreeze(true); - - assertEq(freezer.canFreeze(), true); - } - -} - -contract SparkLendFreezerFreezeAllMarketsTests is SparkLendFreezerUnitTestBase { - - /**********************/ - /*** `freeze` Tests ***/ - /**********************/ - - function test_freezeAllMarkets_notAllowed() public { - vm.prank(ward); - freezer.setCanFreeze(false); - - vm.expectRevert("SparkLendFreezer/freeze-not-allowed"); - freezer.freezeAllMarkets(); - } +contract FreezeAllMarketsTests is SparkLendFreezerUnitTestBase { function test_freezeAllMarkets_noAuth() public { vm.expectRevert("SparkLendFreezer/cannot-call"); freezer.freezeAllMarkets(); } - function test_freezeAllMarkets_cannotCallTwice() public { - authority.__setCanCall( - address(this), - address(freezer), - freezer.freezeAllMarkets.selector, - true - ); - - freezer.freezeAllMarkets(); - - vm.expectRevert("SparkLendFreezer/freeze-not-allowed"); - freezer.freezeAllMarkets(); - } - function test_freezeAllMarkets() public { authority.__setCanCall( address(this), @@ -177,14 +124,35 @@ contract SparkLendFreezerFreezeAllMarketsTests is SparkLendFreezerUnitTestBase { bytes4 poolSig = PoolMock.getReservesList.selector; bytes4 configSig = ConfiguratorMock.setReserveFreeze.selector; - assertEq(freezer.canFreeze(), true); - vm.expectCall(pool, abi.encodePacked(poolSig)); vm.expectCall(configurator, abi.encodePacked(configSig, abi.encode(asset1, true))); vm.expectCall(configurator, abi.encodePacked(configSig, abi.encode(asset2, true))); freezer.freezeAllMarkets(); + } + +} + +contract FreezeMarketTests is SparkLendFreezerUnitTestBase { + + address reserve = makeAddr("reserve"); + + function test_freezeMarket_noAuth() public { + vm.expectRevert("SparkLendFreezer/cannot-call"); + freezer.freezeMarket(reserve); + } + + function test_freezeMarket() public { + authority.__setCanCall( + address(this), + address(freezer), + freezer.freezeMarket.selector, + true + ); + + bytes4 configSig = ConfiguratorMock.setReserveFreeze.selector; - assertEq(freezer.canFreeze(), false); + vm.expectCall(configurator, abi.encodePacked(configSig, abi.encode(reserve, true))); + freezer.freezeMarket(reserve); } } From 9086ad495f846d6af5c78aa09eab7ceaa7f78f5e Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Thu, 9 Nov 2023 09:33:16 -0500 Subject: [PATCH 12/21] fix: update README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 1b62350..32af2a3 100644 --- a/README.md +++ b/README.md @@ -71,3 +71,6 @@ $ forge --help $ anvil --help $ cast --help ``` + +*** +*The IP in this repository was assigned to Mars SPC Limited in respect of the MarsOne SP* From faec6fe9141f86b3a247dbb2b2530a27812f72cb Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Thu, 9 Nov 2023 09:48:46 -0500 Subject: [PATCH 13/21] fix: update to standard structure --- ...endFreezer.sol => SparkLendFreezerMom.sol} | 59 +++++++------ src/interfaces/ISparkLendFreezer.sol | 51 ------------ src/interfaces/ISparkLendFreezerMom.sol | 13 +++ ...reezer.t.sol => SparkLendFreezerMom.t.sol} | 83 +++++++------------ 4 files changed, 78 insertions(+), 128 deletions(-) rename src/{SparkLendFreezer.sol => SparkLendFreezerMom.sol} (62%) delete mode 100644 src/interfaces/ISparkLendFreezer.sol create mode 100644 src/interfaces/ISparkLendFreezerMom.sol rename test/{SparkLendFreezer.t.sol => SparkLendFreezerMom.t.sol} (52%) diff --git a/src/SparkLendFreezer.sol b/src/SparkLendFreezerMom.sol similarity index 62% rename from src/SparkLendFreezer.sol rename to src/SparkLendFreezerMom.sol index 14d4db2..22eb846 100644 --- a/src/SparkLendFreezer.sol +++ b/src/SparkLendFreezerMom.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity ^0.8.13; -import { ISparkLendFreezer } from "src/interfaces/ISparkLendFreezer.sol"; +import { ISparkLendFreezerMom } from "src/interfaces/ISparkLendFreezerMom.sol"; interface AuthorityLike { function canCall(address src, address dst, bytes4 sig) external view returns (bool); @@ -15,7 +15,7 @@ interface PoolLike { function getReservesList() external view returns (address[] memory); } -contract SparkLendFreezer is ISparkLendFreezer { +contract SparkLendFreezerMom is ISparkLendFreezerMom { /**********************************************************************************************/ /*** Declarations and Constructor ***/ @@ -25,32 +25,28 @@ contract SparkLendFreezer is ISparkLendFreezer { address public immutable pool; address public authority; - - mapping (address => uint256) public override wards; + address public owner; constructor(address poolConfigurator_, address pool_, address authority_) { poolConfigurator = poolConfigurator_; pool = pool_; authority = authority_; + owner = msg.sender; - wards[msg.sender] = 1; - emit Rely(msg.sender); + emit SetOwner(address(0), msg.sender); } /**********************************************************************************************/ /*** Modifiers ***/ /**********************************************************************************************/ - modifier auth { - require(wards[msg.sender] == 1, "SparkLendFreezer/not-authorized"); + modifier onlyOwner { + require(msg.sender == owner, "SparkLendFreezerMom/only-owner"); _; } - modifier canCall { - require( - AuthorityLike(authority).canCall(msg.sender, address(this), msg.sig), - "SparkLendFreezer/cannot-call" - ); + modifier auth { + require(isAuthorized(msg.sender, msg.sig), "SparkLendFreezerMom/not-authorized"); _; } @@ -58,27 +54,22 @@ contract SparkLendFreezer is ISparkLendFreezer { /*** Wards Functions ***/ /**********************************************************************************************/ - function deny(address usr) external override auth { - wards[usr] = 0; - emit Deny(usr); + function setAuthority(address authority_) external onlyOwner { + emit SetAuthority(authority, authority_); + authority = authority_; } - function rely(address usr) external override auth { - wards[usr] = 1; - emit Rely(usr); - } - function setAuthority(address authority_) external auth { - address oldAuthority = authority; - authority = authority_; - emit SetAuthority(oldAuthority, authority_); + function setOwner(address owner_) external onlyOwner { + emit SetOwner(owner, owner_); + owner = owner_; } /**********************************************************************************************/ /*** Auth Functions ***/ /**********************************************************************************************/ - function freezeAllMarkets() external canCall { + function freezeAllMarkets() external auth { address[] memory reserves = PoolLike(pool).getReservesList(); for (uint256 i = 0; i < reserves.length; i++) { @@ -87,9 +78,25 @@ contract SparkLendFreezer is ISparkLendFreezer { } } - function freezeMarket(address reserve) external canCall { + function freezeMarket(address reserve) external auth { PoolConfiguratorLike(poolConfigurator).setReserveFreeze(reserve, true); } + /**********************************************************************************************/ + /*** Internal Functions ***/ + /**********************************************************************************************/ + + function isAuthorized(address src, bytes4 sig) internal view returns (bool) { + if (src == address(this)) { + return true; + } else if (src == owner) { + return true; + } else if (authority == address(0)) { + return false; + } else { + return AuthorityLike(authority).canCall(src, address(this), sig); + } + } + } diff --git a/src/interfaces/ISparkLendFreezer.sol b/src/interfaces/ISparkLendFreezer.sol deleted file mode 100644 index ee692f8..0000000 --- a/src/interfaces/ISparkLendFreezer.sol +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.0; - -interface ISparkLendFreezer { - - /**********************************************************************************************/ - /*** Events ***/ - /**********************************************************************************************/ - - event SetOwner(address indexed oldOwner, address indexed newOwner); - event SetAuthority(address indexed oldAuthority, address indexed newAuthority); - - /** - * @dev Event emitted when a new admin is removed from the Conduit. - * @param usr The address of the user to remove. - */ - event Deny(address indexed usr); - - /** - * @dev Event emitted when a new admin is added to the Conduit. - * @param usr The address of the user to add. - */ - event Rely(address indexed usr); - - /**********************************************************************************************/ - /*** Storage Variables ***/ - /**********************************************************************************************/ - - /** - * @dev Returns a 0 or 1 depending on if the user has been added as an admin. - * @return relied The value of the user's admin status. - */ - function wards(address user) external view returns (uint256 relied); - - /**********************************************************************************************/ - /*** Administrative Functions ***/ - /**********************************************************************************************/ - - /** - * @dev Function to remove an addresses admin permissions. - * @param usr The address of the admin. - */ - function deny(address usr) external; - - /** - * @dev Function to give an address admin permissions. - * @param usr The address of the new admin. - */ - function rely(address usr) external; - -} diff --git a/src/interfaces/ISparkLendFreezerMom.sol b/src/interfaces/ISparkLendFreezerMom.sol new file mode 100644 index 0000000..0d43a53 --- /dev/null +++ b/src/interfaces/ISparkLendFreezerMom.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.0; + +interface ISparkLendFreezerMom { + + /**********************************************************************************************/ + /*** Events ***/ + /**********************************************************************************************/ + + event SetOwner(address indexed oldOwner, address indexed newOwner); + event SetAuthority(address indexed oldAuthority, address indexed newAuthority); + +} diff --git a/test/SparkLendFreezer.t.sol b/test/SparkLendFreezerMom.t.sol similarity index 52% rename from test/SparkLendFreezer.t.sol rename to test/SparkLendFreezerMom.t.sol index 4a27f7c..107d204 100644 --- a/test/SparkLendFreezer.t.sol +++ b/test/SparkLendFreezerMom.t.sol @@ -3,96 +3,77 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; -import { SparkLendFreezer } from "../src/SparkLendFreezer.sol"; +import { SparkLendFreezerMom } from "../src/SparkLendFreezerMom.sol"; import { AuthorityMock, ConfiguratorMock, PoolMock } from "./Mocks.sol"; -contract SparkLendFreezerUnitTestBase is Test { +contract SparkLendFreezerMomUnitTestBase is Test { address public configurator; address public pool; - address public ward; + address public owner; AuthorityMock public authority; - SparkLendFreezer public freezer; + SparkLendFreezerMom public freezer; function setUp() public { - ward = makeAddr("ward"); + owner = makeAddr("owner"); authority = new AuthorityMock(); configurator = address(new ConfiguratorMock()); pool = address(new PoolMock()); - freezer = new SparkLendFreezer(configurator, pool, address(authority)); + freezer = new SparkLendFreezerMom(configurator, pool, address(authority)); - freezer.rely(ward); - freezer.deny(address(this)); + freezer.setOwner(owner); } } -contract ConstructorTests is SparkLendFreezerUnitTestBase { +contract ConstructorTests is SparkLendFreezerMomUnitTestBase { function test_constructor() public { - freezer = new SparkLendFreezer(configurator, pool, address(authority)); + freezer = new SparkLendFreezerMom(configurator, pool, address(authority)); - assertEq(freezer.poolConfigurator(), configurator); - assertEq(freezer.pool(), pool); - assertEq(freezer.authority(), address(authority)); - assertEq(freezer.wards(address(this)), 1); + assertEq(freezer.poolConfigurator(), configurator); + assertEq(freezer.pool(), pool); + assertEq(freezer.authority(), address(authority)); + assertEq(freezer.owner(), address(this)); } } -contract DenyTests is SparkLendFreezerUnitTestBase { +contract SetOwnerTests is SparkLendFreezerMomUnitTestBase { - function test_deny_no_auth() public { - vm.expectRevert("SparkLendFreezer/not-authorized"); - freezer.deny(ward); + function test_setOwner_no_auth() public { + vm.expectRevert("SparkLendFreezerMom/only-owner"); + freezer.setOwner(address(1)); } - function test_deny() public { - assertEq(freezer.wards(ward), 1); + function test_setOwner() public { + address newOwner = makeAddr("newOwner"); + assertEq(freezer.owner(), owner); - vm.prank(ward); - freezer.deny(ward); + vm.prank(owner); + freezer.setOwner(newOwner); - assertEq(freezer.wards(ward), 0); + assertEq(freezer.owner(), newOwner); } } -contract RelyTests is SparkLendFreezerUnitTestBase { - - function test_rely_no_auth() public { - vm.expectRevert("SparkLendFreezer/not-authorized"); - freezer.rely(makeAddr("new ward")); - } - - function test_rely() public { - address newWard = makeAddr("new ward"); - assertEq(freezer.wards(newWard), 0); - - vm.prank(ward); - freezer.rely(newWard); - - assertEq(freezer.wards(ward), 1); - } - -} - -contract SetAuthorityTests is SparkLendFreezerUnitTestBase { +contract SetAuthorityTests is SparkLendFreezerMomUnitTestBase { function test_setAuthority_no_auth() public { - vm.expectRevert("SparkLendFreezer/not-authorized"); - freezer.setAuthority(makeAddr("new authority")); + vm.expectRevert("SparkLendFreezerMom/only-owner"); + freezer.setAuthority(makeAddr("newAuthority")); } function test_setAuthority() public { - address newAuthority = makeAddr("new authority"); + address newAuthority = makeAddr("newAuthority"); assertEq(freezer.authority(), address(authority)); - vm.prank(ward); + vm.prank(owner); freezer.setAuthority(newAuthority); assertEq(freezer.authority(), newAuthority); @@ -100,10 +81,10 @@ contract SetAuthorityTests is SparkLendFreezerUnitTestBase { } -contract FreezeAllMarketsTests is SparkLendFreezerUnitTestBase { +contract FreezeAllMarketsTests is SparkLendFreezerMomUnitTestBase { function test_freezeAllMarkets_noAuth() public { - vm.expectRevert("SparkLendFreezer/cannot-call"); + vm.expectRevert("SparkLendFreezerMom/not-authorized"); freezer.freezeAllMarkets(); } @@ -132,12 +113,12 @@ contract FreezeAllMarketsTests is SparkLendFreezerUnitTestBase { } -contract FreezeMarketTests is SparkLendFreezerUnitTestBase { +contract FreezeMarketTests is SparkLendFreezerMomUnitTestBase { address reserve = makeAddr("reserve"); function test_freezeMarket_noAuth() public { - vm.expectRevert("SparkLendFreezer/cannot-call"); + vm.expectRevert("SparkLendFreezerMom/not-authorized"); freezer.freezeMarket(reserve); } From 58dc680884f8f8c55afcbe1dac5247f87f664b33 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Thu, 9 Nov 2023 09:57:27 -0500 Subject: [PATCH 14/21] feat: add natspec and overrides --- src/SparkLendFreezerMom.sol | 8 +-- src/interfaces/ISparkLendFreezerMom.sol | 70 +++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/SparkLendFreezerMom.sol b/src/SparkLendFreezerMom.sol index 22eb846..edf49d1 100644 --- a/src/SparkLendFreezerMom.sol +++ b/src/SparkLendFreezerMom.sol @@ -54,13 +54,13 @@ contract SparkLendFreezerMom is ISparkLendFreezerMom { /*** Wards Functions ***/ /**********************************************************************************************/ - function setAuthority(address authority_) external onlyOwner { + function setAuthority(address authority_) external override onlyOwner { emit SetAuthority(authority, authority_); authority = authority_; } - function setOwner(address owner_) external onlyOwner { + function setOwner(address owner_) external override onlyOwner { emit SetOwner(owner, owner_); owner = owner_; } @@ -69,7 +69,7 @@ contract SparkLendFreezerMom is ISparkLendFreezerMom { /*** Auth Functions ***/ /**********************************************************************************************/ - function freezeAllMarkets() external auth { + function freezeAllMarkets() external override auth { address[] memory reserves = PoolLike(pool).getReservesList(); for (uint256 i = 0; i < reserves.length; i++) { @@ -78,7 +78,7 @@ contract SparkLendFreezerMom is ISparkLendFreezerMom { } } - function freezeMarket(address reserve) external auth { + function freezeMarket(address reserve) external override auth { PoolConfiguratorLike(poolConfigurator).setReserveFreeze(reserve, true); } diff --git a/src/interfaces/ISparkLendFreezerMom.sol b/src/interfaces/ISparkLendFreezerMom.sol index 0d43a53..ebed816 100644 --- a/src/interfaces/ISparkLendFreezerMom.sol +++ b/src/interfaces/ISparkLendFreezerMom.sol @@ -7,7 +7,77 @@ interface ISparkLendFreezerMom { /*** Events ***/ /**********************************************************************************************/ + /** + * @dev Event to log the setting of a new owner. + * @param oldOwner The address of the previous owner. + * @param newOwner The address of the new owner. + */ event SetOwner(address indexed oldOwner, address indexed newOwner); + + /** + * @dev Event to log the setting of a new authority. + * @param oldAuthority The address of the previous authority. + * @param newAuthority The address of the new authority. + */ event SetAuthority(address indexed oldAuthority, address indexed newAuthority); + /**********************************************************************************************/ + /*** Storage Variables ***/ + /**********************************************************************************************/ + + /** + * @dev Returns the address of the pool configurator. + * @return The address of the pool configurator. + */ + function poolConfigurator() external view returns (address); + + /** + * @dev Returns the address of the pool. + * @return The address of the pool. + */ + function pool() external view returns (address); + + /** + * @dev Returns the address of the authority. + * @return The address of the authority. + */ + function authority() external view returns (address); + + /** + * @dev Returns the address of the owner. + * @return The address of the owner. + */ + function owner() external view returns (address); + + /**********************************************************************************************/ + /*** Owner Functions ***/ + /**********************************************************************************************/ + + /** + * @dev Function to set a new authority, permissioned to owner. + * @param authority The address of the new authority. + */ + function setAuthority(address authority) external; + + /** + * @dev Function to set a new owner, permissioned to owner. + * @param owner The address of the new owner. + */ + function setOwner(address owner) external; + + /**********************************************************************************************/ + /*** Auth Functions ***/ + /**********************************************************************************************/ + + /** + * @dev Function to freeze a specified market. Permissioned to the `hat` in the Chief. + * @param reserve The address of the market to freeze. + */ + function freezeMarket(address reserve) external; + + /** + * @dev Function to freeze all markets. Permissioned to the `hat` in the Chief. + */ + function freezeAllMarkets() external; + } From 21af6aa2f350c7b176cd1aa15214aaeb4056e9d3 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Fri, 10 Nov 2023 11:20:03 -0500 Subject: [PATCH 15/21] fix: rm authority from constructor --- src/SparkLendFreezerMom.sol | 3 +-- test/SparkLendFreezerMom.t.sol | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/SparkLendFreezerMom.sol b/src/SparkLendFreezerMom.sol index edf49d1..89cb865 100644 --- a/src/SparkLendFreezerMom.sol +++ b/src/SparkLendFreezerMom.sol @@ -27,10 +27,9 @@ contract SparkLendFreezerMom is ISparkLendFreezerMom { address public authority; address public owner; - constructor(address poolConfigurator_, address pool_, address authority_) { + constructor(address poolConfigurator_, address pool_) { poolConfigurator = poolConfigurator_; pool = pool_; - authority = authority_; owner = msg.sender; emit SetOwner(address(0), msg.sender); diff --git a/test/SparkLendFreezerMom.t.sol b/test/SparkLendFreezerMom.t.sol index 107d204..49875dc 100644 --- a/test/SparkLendFreezerMom.t.sol +++ b/test/SparkLendFreezerMom.t.sol @@ -23,8 +23,9 @@ contract SparkLendFreezerMomUnitTestBase is Test { authority = new AuthorityMock(); configurator = address(new ConfiguratorMock()); pool = address(new PoolMock()); - freezer = new SparkLendFreezerMom(configurator, pool, address(authority)); + freezer = new SparkLendFreezerMom(configurator, pool); + freezer.setAuthority(address(authority)); freezer.setOwner(owner); } @@ -33,11 +34,10 @@ contract SparkLendFreezerMomUnitTestBase is Test { contract ConstructorTests is SparkLendFreezerMomUnitTestBase { function test_constructor() public { - freezer = new SparkLendFreezerMom(configurator, pool, address(authority)); + freezer = new SparkLendFreezerMom(configurator, pool); assertEq(freezer.poolConfigurator(), configurator); assertEq(freezer.pool(), pool); - assertEq(freezer.authority(), address(authority)); assertEq(freezer.owner(), address(this)); } From b8e6669f6cb847a2faca6f335f9dc42134c01b9d Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Fri, 10 Nov 2023 11:23:48 -0500 Subject: [PATCH 16/21] fix: update comments --- src/SparkLendFreezerMom.sol | 3 +-- src/interfaces/ISparkLendFreezerMom.sol | 8 ++++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/SparkLendFreezerMom.sol b/src/SparkLendFreezerMom.sol index 89cb865..2b648a5 100644 --- a/src/SparkLendFreezerMom.sol +++ b/src/SparkLendFreezerMom.sol @@ -50,7 +50,7 @@ contract SparkLendFreezerMom is ISparkLendFreezerMom { } /**********************************************************************************************/ - /*** Wards Functions ***/ + /*** Owner Functions ***/ /**********************************************************************************************/ function setAuthority(address authority_) external override onlyOwner { @@ -72,7 +72,6 @@ contract SparkLendFreezerMom is ISparkLendFreezerMom { address[] memory reserves = PoolLike(pool).getReservesList(); for (uint256 i = 0; i < reserves.length; i++) { - if (reserves[i] == address(0)) continue; PoolConfiguratorLike(poolConfigurator).setReserveFreeze(reserves[i], true); } } diff --git a/src/interfaces/ISparkLendFreezerMom.sol b/src/interfaces/ISparkLendFreezerMom.sol index ebed816..2c2e06e 100644 --- a/src/interfaces/ISparkLendFreezerMom.sol +++ b/src/interfaces/ISparkLendFreezerMom.sol @@ -70,13 +70,17 @@ interface ISparkLendFreezerMom { /**********************************************************************************************/ /** - * @dev Function to freeze a specified market. Permissioned to the `hat` in the Chief. + * @dev Function to freeze a specified market. Permissioned using the isAuthorized function + * which allows the owner, the freezer contract itself, or the `hat` in the Chief + * to call the function. * @param reserve The address of the market to freeze. */ function freezeMarket(address reserve) external; /** - * @dev Function to freeze all markets. Permissioned to the `hat` in the Chief. + * @dev Function to freeze all markets. Permissioned using the isAuthorized function + * which allows the owner, the freezer contract itself, or the `hat` in the Chief + * to call the function. */ function freezeAllMarkets() external; From c64a86befb3aff2b89ce818391ebddbae4d22427 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Fri, 10 Nov 2023 11:25:47 -0500 Subject: [PATCH 17/21] fix: update to use specified caller, uniform tests --- test/SparkLendFreezerMom.t.sol | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/SparkLendFreezerMom.t.sol b/test/SparkLendFreezerMom.t.sol index 49875dc..2f917ba 100644 --- a/test/SparkLendFreezerMom.t.sol +++ b/test/SparkLendFreezerMom.t.sol @@ -45,7 +45,7 @@ contract ConstructorTests is SparkLendFreezerMomUnitTestBase { contract SetOwnerTests is SparkLendFreezerMomUnitTestBase { - function test_setOwner_no_auth() public { + function test_setOwner_noAuth() public { vm.expectRevert("SparkLendFreezerMom/only-owner"); freezer.setOwner(address(1)); } @@ -64,7 +64,7 @@ contract SetOwnerTests is SparkLendFreezerMomUnitTestBase { contract SetAuthorityTests is SparkLendFreezerMomUnitTestBase { - function test_setAuthority_no_auth() public { + function test_setAuthority_noAuth() public { vm.expectRevert("SparkLendFreezerMom/only-owner"); freezer.setAuthority(makeAddr("newAuthority")); } @@ -89,8 +89,10 @@ contract FreezeAllMarketsTests is SparkLendFreezerMomUnitTestBase { } function test_freezeAllMarkets() public { + address caller = makeAddr("caller"); + authority.__setCanCall( - address(this), + caller, address(freezer), freezer.freezeAllMarkets.selector, true @@ -105,6 +107,7 @@ contract FreezeAllMarketsTests is SparkLendFreezerMomUnitTestBase { bytes4 poolSig = PoolMock.getReservesList.selector; bytes4 configSig = ConfiguratorMock.setReserveFreeze.selector; + vm.prank(caller); vm.expectCall(pool, abi.encodePacked(poolSig)); vm.expectCall(configurator, abi.encodePacked(configSig, abi.encode(asset1, true))); vm.expectCall(configurator, abi.encodePacked(configSig, abi.encode(asset2, true))); @@ -123,8 +126,10 @@ contract FreezeMarketTests is SparkLendFreezerMomUnitTestBase { } function test_freezeMarket() public { + address caller = makeAddr("caller"); + authority.__setCanCall( - address(this), + caller, address(freezer), freezer.freezeMarket.selector, true @@ -132,6 +137,7 @@ contract FreezeMarketTests is SparkLendFreezerMomUnitTestBase { bytes4 configSig = ConfiguratorMock.setReserveFreeze.selector; + vm.prank(caller); vm.expectCall(configurator, abi.encodePacked(configSig, abi.encode(reserve, true))); freezer.freezeMarket(reserve); } From 3bdc955d9adc6f793d418f3ef33df707b64fa221 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Fri, 10 Nov 2023 11:34:52 -0500 Subject: [PATCH 18/21] feat: update to add freeze market events, events testing --- src/SparkLendFreezerMom.sol | 2 + src/interfaces/ISparkLendFreezerMom.sol | 6 +++ test/SparkLendFreezerMom.t.sol | 65 +++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/src/SparkLendFreezerMom.sol b/src/SparkLendFreezerMom.sol index 2b648a5..1fab45d 100644 --- a/src/SparkLendFreezerMom.sol +++ b/src/SparkLendFreezerMom.sol @@ -73,11 +73,13 @@ contract SparkLendFreezerMom is ISparkLendFreezerMom { for (uint256 i = 0; i < reserves.length; i++) { PoolConfiguratorLike(poolConfigurator).setReserveFreeze(reserves[i], true); + emit FreezeMarket(reserves[i]); } } function freezeMarket(address reserve) external override auth { PoolConfiguratorLike(poolConfigurator).setReserveFreeze(reserve, true); + emit FreezeMarket(reserve); } /**********************************************************************************************/ diff --git a/src/interfaces/ISparkLendFreezerMom.sol b/src/interfaces/ISparkLendFreezerMom.sol index 2c2e06e..6aaeb99 100644 --- a/src/interfaces/ISparkLendFreezerMom.sol +++ b/src/interfaces/ISparkLendFreezerMom.sol @@ -7,6 +7,12 @@ interface ISparkLendFreezerMom { /*** Events ***/ /**********************************************************************************************/ + /** + * @dev Event to log the freezing of a given market in SparkLend. + * @param reserve The address of the market reserve. + */ + event FreezeMarket(address indexed reserve); + /** * @dev Event to log the setting of a new owner. * @param oldOwner The address of the previous owner. diff --git a/test/SparkLendFreezerMom.t.sol b/test/SparkLendFreezerMom.t.sol index 2f917ba..df2ae1e 100644 --- a/test/SparkLendFreezerMom.t.sol +++ b/test/SparkLendFreezerMom.t.sol @@ -144,4 +144,69 @@ contract FreezeMarketTests is SparkLendFreezerMomUnitTestBase { } +contract EventTests is SparkLendFreezerMomUnitTestBase { + + event FreezeMarket(address indexed reserve); + event SetOwner(address indexed oldOwner, address indexed newOwner); + event SetAuthority(address indexed oldAuthority, address indexed newAuthority); + + function test_setAuthority_eventData() external { + address newAuthority = makeAddr("newAuthority"); + + vm.prank(owner); + vm.expectEmit(address(freezer)); + emit SetAuthority(address(authority), newAuthority); + freezer.setAuthority(newAuthority); + } + + function test_setOwner_eventData() external { + address newOwner = makeAddr("newOwner"); + + vm.prank(owner); + vm.expectEmit(address(freezer)); + emit SetOwner(owner, newOwner); + freezer.setOwner(newOwner); + } + + function test_freezeMarket_eventData() public { + address caller = makeAddr("caller"); + address asset = makeAddr("asset"); + + authority.__setCanCall( + caller, + address(freezer), + freezer.freezeMarket.selector, + true + ); + + vm.prank(caller); + emit FreezeMarket(asset); + freezer.freezeMarket(asset); + } + + function test_freezeAllMarkets_eventData() public { + address caller = makeAddr("caller"); + + authority.__setCanCall( + caller, + address(freezer), + freezer.freezeAllMarkets.selector, + true + ); + + address asset1 = makeAddr("asset1"); + address asset2 = makeAddr("asset2"); + + PoolMock(pool).__addAsset(asset1); + PoolMock(pool).__addAsset(asset2); + + vm.prank(caller); + vm.expectEmit(address(freezer)); + emit FreezeMarket(asset1); + vm.expectEmit(address(freezer)); + emit FreezeMarket(asset2); + freezer.freezeAllMarkets(); + } +} + From 21e0704f04bbeef380dc09bb137f37f5ac831ae9 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Fri, 10 Nov 2023 11:36:23 -0500 Subject: [PATCH 19/21] fix: alignment --- test/SparkLendFreezerMom.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/SparkLendFreezerMom.t.sol b/test/SparkLendFreezerMom.t.sol index df2ae1e..2b6524b 100644 --- a/test/SparkLendFreezerMom.t.sol +++ b/test/SparkLendFreezerMom.t.sol @@ -169,8 +169,8 @@ contract EventTests is SparkLendFreezerMomUnitTestBase { } function test_freezeMarket_eventData() public { - address caller = makeAddr("caller"); - address asset = makeAddr("asset"); + address caller = makeAddr("caller"); + address asset = makeAddr("asset"); authority.__setCanCall( caller, From c7411133a397115cecda7d901e5b50437cb86a55 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Sun, 12 Nov 2023 18:53:06 -0500 Subject: [PATCH 20/21] fix: update as per review --- src/SparkLendFreezerMom.sol | 5 +++-- src/interfaces/ISparkLendFreezerMom.sol | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/SparkLendFreezerMom.sol b/src/SparkLendFreezerMom.sol index 1fab45d..042f3ba 100644 --- a/src/SparkLendFreezerMom.sol +++ b/src/SparkLendFreezerMom.sol @@ -72,8 +72,9 @@ contract SparkLendFreezerMom is ISparkLendFreezerMom { address[] memory reserves = PoolLike(pool).getReservesList(); for (uint256 i = 0; i < reserves.length; i++) { - PoolConfiguratorLike(poolConfigurator).setReserveFreeze(reserves[i], true); - emit FreezeMarket(reserves[i]); + address reserve = reserves[i]; + PoolConfiguratorLike(poolConfigurator).setReserveFreeze(reserve, true); + emit FreezeMarket(reserve); } } diff --git a/src/interfaces/ISparkLendFreezerMom.sol b/src/interfaces/ISparkLendFreezerMom.sol index 6aaeb99..5270fb1 100644 --- a/src/interfaces/ISparkLendFreezerMom.sol +++ b/src/interfaces/ISparkLendFreezerMom.sol @@ -9,6 +9,7 @@ interface ISparkLendFreezerMom { /** * @dev Event to log the freezing of a given market in SparkLend. + * @dev NOTE: This event will fire even if the market is already frozen. * @param reserve The address of the market reserve. */ event FreezeMarket(address indexed reserve); @@ -78,7 +79,8 @@ interface ISparkLendFreezerMom { /** * @dev Function to freeze a specified market. Permissioned using the isAuthorized function * which allows the owner, the freezer contract itself, or the `hat` in the Chief - * to call the function. + * to call the function. Note that the `authority` in this contract is assumed to be + * the Chief in the MakerDAO protocol. * @param reserve The address of the market to freeze. */ function freezeMarket(address reserve) external; From c11c530f0afb61b41a2c69765cd4543d4bbb07ca Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Mon, 13 Nov 2023 09:26:52 -0500 Subject: [PATCH 21/21] fix: update natspec --- src/interfaces/ISparkLendFreezerMom.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/interfaces/ISparkLendFreezerMom.sol b/src/interfaces/ISparkLendFreezerMom.sol index 5270fb1..1ed3c54 100644 --- a/src/interfaces/ISparkLendFreezerMom.sol +++ b/src/interfaces/ISparkLendFreezerMom.sol @@ -88,7 +88,8 @@ interface ISparkLendFreezerMom { /** * @dev Function to freeze all markets. Permissioned using the isAuthorized function * which allows the owner, the freezer contract itself, or the `hat` in the Chief - * to call the function. + * to call the function. Note that the `authority` in this contract is assumed to be + * the Chief in the MakerDAO protocol. */ function freezeAllMarkets() external;