Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: initial structure for covering all key scenarios #1

Merged
merged 9 commits into from
Aug 28, 2024
28 changes: 24 additions & 4 deletions src/AuraLockerModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ contract AuraLockerModule is
ERRORS
//////////////////////////////////////////////////////////////////////////*/
error NotKeeper(address agent);
error NotGovernance(address agent);

error ZeroAddressValue();

error ModuleNotEnabled();

error TxFromModuleFailed();

error NothingToLock(uint256 timestamp);

/*//////////////////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////////////////*/
Expand All @@ -61,13 +64,19 @@ contract AuraLockerModule is
_;
}

/// @notice Enforce that the function is called by governance only
modifier onlyGovernance() {
if (msg.sender != BALANCER_MULTISIG) revert NotGovernance(msg.sender);
_;
}

/*//////////////////////////////////////////////////////////////////////////
EXTERNAL METHODS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Assigns a new keeper address
/// @param _keeper The address of the new keeper
function setKeeper(address _keeper) external {
function setKeeper(address _keeper) external onlyGovernance {
if (_keeper == address(0)) revert ZeroAddressValue();

address oldKeeper = keeper;
Expand All @@ -85,8 +94,7 @@ contract AuraLockerModule is
override
returns (bool requiresLocking, bytes memory execPayload)
{
// if (!SAFE.isModuleEnabled(address(this)))
// return (false, bytes("AuraLocker module is not enabled"));
if (!_isModuleEnabled()) return (false, bytes("AuraLocker module is not enabled"));

(, uint256 unlockable,,) = AURA_LOCKER.lockedBalances(address(SAFE));

Expand All @@ -99,7 +107,10 @@ contract AuraLockerModule is

/// @notice The actual execution of the action determined by the `checkUpkeep` method (AURA locking)
function performUpkeep(bytes calldata /* _performData */ ) external override onlyKeeper {
// if (!SAFE.isModuleEnabled(address(this))) revert ModuleNotEnabled();
if (!_isModuleEnabled()) revert ModuleNotEnabled();

(, uint256 unlockable,,) = AURA_LOCKER.lockedBalances(address(SAFE));
if (unlockable == 0) revert NothingToLock(block.timestamp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im guessing formally reverting it will prevent chainlink from making the actual call, and thus saving gas?


// execute: `processExpiredLocks` via module
if (
Expand All @@ -108,4 +119,13 @@ contract AuraLockerModule is
)
) revert TxFromModuleFailed();
}

/// @dev The Gnosis Safe version 1.1.1 does not expose directly `isModuleEnabled` method, so we need a workaround
function _isModuleEnabled() internal view returns (bool) {
address[] memory modules = SAFE.getModules();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getModules() is limited to the first ten modules in the list, but i think that should be fine for now

for (uint256 i = 0; i < modules.length; i++) {
if (modules[i] == address(this)) return true;
}
return false;
}
}
50 changes: 46 additions & 4 deletions test/AuraLockerModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {ILockAura} from "../src/interfaces/aura/ILockAura.sol";
import {AuraLockerModule} from "../src/AuraLockerModule.sol";

contract AuraLockerModuleTest is BaseFixture {
function test_checkUpkeep_when_NotNewUnlock() public {
function test_checkUpkeep_when_NotNewUnlock() public view {
// at present nothing to lock
(bool requiresLocking, bytes memory execPayload) = auraLockerModule.checkUpkeep(bytes(""));
assertFalse(requiresLocking);
Expand All @@ -27,16 +27,58 @@ contract AuraLockerModuleTest is BaseFixture {
// `disableModule(address prevModule, address module)`
vm.prank(address(SAFE));
SAFE.disableModule(address(1), address(auraLockerModule));
// assertFalse(SAFE.isModuleEnabled(address(auraLockerModule)));
address[] memory modules = SAFE.getModules();
for (uint256 i = 0; i < modules.length; i++) {
if (modules[i] == address(auraLockerModule)) assertFalse(true);
}

// once module its removed, the keeper trying to call `performUpkeep` should revert
vm.prank(auraLockerModule.keeper());
vm.expectRevert(abi.encodeWithSelector(AuraLockerModule.ModuleNotEnabled.selector));
auraLockerModule.performUpkeep(bytes(""));
}

function test_revertWhen_NothingToUnlock() public {}
function testPerformUpkeep_revertWhen_NothingToLock() public {
// force a `performUpkeep` when weeks did not go by
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"when not enough weeks went by"?

skip(1 weeks);

vm.prank(auraLockerModule.keeper());
vm.expectRevert(abi.encodeWithSelector(AuraLockerModule.NothingToLock.selector, block.timestamp));
auraLockerModule.performUpkeep(bytes(""));
}

function testPerformUpkeepSuccess() public {
// move to future
skip(16 weeks);
(bool requiresLocking,) = auraLockerModule.checkUpkeep(bytes(""));
assertTrue(requiresLocking);

(uint256 totalAuraInLocker,, uint256 lockedBeforePerformUpkeep,) = AURA_LOCKER.lockedBalances(address(SAFE));

function testPerformUpkeepSuccess() public {}
vm.prank(auraLockerModule.keeper());
auraLockerModule.performUpkeep(bytes(""));

// check 2M where locked properly
(,, uint256 lockedAfterPerformUpkeep,) = AURA_LOCKER.lockedBalances(address(SAFE));
assertGt(lockedAfterPerformUpkeep, lockedBeforePerformUpkeep);
assertEq(totalAuraInLocker, lockedAfterPerformUpkeep);
}

function testPerformUpkeep_revertWhen_NotKeeper() public {
vm.prank(address(454545));
vm.expectRevert(abi.encodeWithSelector(AuraLockerModule.NotKeeper.selector, address(454545)));
auraLockerModule.performUpkeep(bytes(""));
}

function testSetKeeper_revertWhen_NotGovernance() public {
vm.prank(address(454545));
vm.expectRevert(abi.encodeWithSelector(AuraLockerModule.NotGovernance.selector, address(454545)));
auraLockerModule.setKeeper(address(454545));
}

function testSetKeeper_revertWhen_AddressIsZero() public {
vm.prank(address(SAFE));
vm.expectRevert(abi.encodeWithSelector(AuraLockerModule.ZeroAddressValue.selector));
auraLockerModule.setKeeper(address(0));
}
}
3 changes: 2 additions & 1 deletion test/BaseFixture.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ contract BaseFixture is Test {
uint256 upkeepId = CL_REGISTRAR.registerUpkeep(registrationParams);
address keeper = CL_REGISTRY.getForwarder(upkeepId);
assertNotEq(keeper, address(0));
vm.stopPrank();

vm.prank(address(SAFE));
auraLockerModule.setKeeper(keeper);
assertEq(auraLockerModule.keeper(), keeper);
vm.stopPrank();

_labelKeyContracts();
}
Expand Down