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

feat: add swap owner to allowlist, test #37

Merged
merged 4 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions safe-batches/enable-swap-owner.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": "1.0",
"chainId": "1",
"createdAt": 1738828295179,
"meta": {
"name": "Transactions Batch",
"description": "enable-swap-owner",
"txBuilderVersion": "1.10.0",
"createdFromSafeAddress": "0x9D89745fD63Af482ce93a9AdB8B0BbDbb98D3e06",
"createdFromOwnerAddress": "",
"checksum": "0x102a3dcd91b01f97351f36f92ab491ab09e4b218f3525dc41c30cd1e89eb820c"
},
"transactions": [
{
"to": "0x9000fef2846a5253fd2c6ed5241de0fddb404302",
"value": "0x0",
"data": "0x5534fa0c0000000000000000000000000000000000000000000000000000000000000000e318b52b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"
}
]
}
24 changes: 23 additions & 1 deletion script/RumpelConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ library RumpelConfig {
return new ProtocolGuardConfig[](0);
} else if (tagHash == keccak256(bytes("pendle-usde-yts"))) {
return new ProtocolGuardConfig[](0);
} else if (tagHash == keccak256(bytes("enable-swap-owner"))) {
return getEnableSwapOwnerProtocolGuard();
}

revert("Unsupported tag");
Expand Down Expand Up @@ -308,6 +310,8 @@ library RumpelConfig {
return getPermAllowMarchAndMay2025SusdeYTsTokenGuardConfigs();
} else if (tagHash == keccak256(bytes("pendle-usde-yts"))) {
return getPendleUSDEYTsTokenGuardConfigs();
} else if (tagHash == keccak256(bytes("enable-swap-owner"))) {
return new TokenGuardConfig[](0);
}

revert("Unsupported tag");
Expand Down Expand Up @@ -359,6 +363,8 @@ library RumpelConfig {
return getMarchAndMay20252025SusdeYTsTokenModuleConfigs();
} else if (tagHash == keccak256(bytes("pendle-usde-yts"))) {
return new TokenModuleConfig[](0);
} else if (tagHash == keccak256(bytes("enable-swap-owner"))) {
return new TokenModuleConfig[](0);
}

revert("Unsupported tag");
Expand Down Expand Up @@ -404,9 +410,11 @@ library RumpelConfig {
} else if (tagHash == keccak256(bytes("remove-lrt2-claiming"))) {
return new ProtocolModuleConfig[](0);
} else if (tagHash == keccak256(bytes("perm-allow-march-may-2025-susde-yts"))) {
return new ProtocolModuleConfig[](0);
return new ProtocolModuleConfig[](0);
} else if (tagHash == keccak256(bytes("pendle-usde-yts"))) {
return new ProtocolModuleConfig[](0);
} else if (tagHash == keccak256(bytes("enable-swap-owner"))) {
return new ProtocolModuleConfig[](0);
}

revert("Unsupported tag");
Expand Down Expand Up @@ -1246,6 +1254,16 @@ library RumpelConfig {

return configs;
}

function getEnableSwapOwnerProtocolGuard() internal pure returns (ProtocolGuardConfig[] memory) {
ProtocolGuardConfig[] memory configs = new ProtocolGuardConfig[](1);

configs[0] = ProtocolGuardConfig({target: address(0), selectorStates: new SelectorState[](1)});
configs[0].selectorStates[0] =
SelectorState({selector: Safe.swapOwner.selector, state: RumpelGuard.AllowListState.ON});

return configs;
}
}

interface IMorphoBundler {
Expand Down Expand Up @@ -1394,3 +1412,7 @@ interface ILRT2Claim {
bytes32[] calldata merkleProof
) external;
}

interface Safe {
function swapOwner(address prevOwner, address oldOwner, address newOwner) external;
}
4 changes: 4 additions & 0 deletions src/interfaces/external/ISafe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ interface ISafe {

function addOwnerWithThreshold(address owner, uint256 _threshold) external;

function removeOwner(address prevOwner, address owner, uint256 _threshold) external;

function swapOwner(address prevOwner, address oldOwner, address newOwner) external;

Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't work so nicely with my print-state script, since it knows functions in the config contract but not sure we should make decisions based on that.

I can also update the print functionality to also get all functions from contracts in the src folder

Copy link
Contributor Author

@jparklev jparklev Feb 6, 2025

Choose a reason for hiding this comment

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

easy enough to change – moved it into the config file

// Fallback handler functions

function isValidSignature(bytes32 _dataHash, bytes calldata _signature) external view returns (bytes4);
Expand Down
85 changes: 85 additions & 0 deletions test/RumpelWalletSwapOwner.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity =0.8.24;

import {Test} from "forge-std/Test.sol";

import {RumpelGuard} from "../src/RumpelGuard.sol";
import {RumpelModule} from "../src/RumpelModule.sol";
import {InitializationScript} from "../src/InitializationScript.sol";
import {RumpelWalletFactory} from "../src/RumpelWalletFactory.sol";

import {ISafe, Enum} from "../src/interfaces/external/ISafe.sol";
import {RumpelWalletFactoryScripts} from "../script/RumpelWalletFactory.s.sol";

contract RumpelWalletSwapOwnerTest is Test {
RumpelModule public rumpelModule = RumpelModule(0x28c3498B4956f4aD8d4549ACA8F66260975D361a);
RumpelGuard public rumpelGuard = RumpelGuard(0x9000FeF2846A5253fD2C6ed5241De0fddb404302);
RumpelWalletFactory public rumpelWalletFactory = RumpelWalletFactory(0x5774AbCF415f34592514698EB075051E97Db2937);

address alice;
uint256 alicePk;

struct SafeTX {
address to;
uint256 value;
bytes data;
Enum.Operation operation;
}

function setUp() public {
(alice, alicePk) = makeAddrAndKey("alice");

string memory MAINNET_RPC_URL = vm.envString("MAINNET_RPC_URL");
uint256 FORK_BLOCK_NUMBER = 21748243; // Feb-01-2025 12:59:47 AM +UTC
vm.createSelectFork(MAINNET_RPC_URL, FORK_BLOCK_NUMBER);
}

function test_SwapOwner() public {
RumpelWalletFactoryScripts scripts = new RumpelWalletFactoryScripts();

address[] memory owners = new address[](1);
owners[0] = address(alice);

InitializationScript.InitCall[] memory initCalls = new InitializationScript.InitCall[](0);
address safe = rumpelWalletFactory.createWallet(owners, 1, initCalls);

address NEW_OWNER = address(0x123);

assertEq(ISafe(safe).getOwners().length, 1);
assertEq(ISafe(safe).getOwners()[0], address(alice));

bytes memory swapOwnerData =
abi.encodeWithSelector(ISafe.swapOwner.selector, address(0x1), address(alice), NEW_OWNER);
vm.expectRevert(
abi.encodeWithSelector(RumpelGuard.CallNotAllowed.selector, address(safe), bytes4(swapOwnerData))
);
this._execSafeTx(ISafe(safe), safe, 0, swapOwnerData, Enum.Operation.Call);

assertEq(ISafe(safe).getOwners().length, 1);
assertEq(ISafe(safe).getOwners()[0], address(alice));

// Run the script to enable the swapOwner call
scripts.updateGuardAndModuleLists(rumpelGuard, rumpelModule, "enable-swap-owner");

this._execSafeTx(ISafe(safe), safe, 0, swapOwnerData, Enum.Operation.Call);

assertEq(ISafe(safe).getOwners().length, 1);
assertEq(ISafe(safe).getOwners()[0], NEW_OWNER);
}

function _execSafeTx(ISafe safe, address to, uint256 value, bytes memory data, Enum.Operation operation) public {
Copy link
Contributor

Choose a reason for hiding this comment

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

A future clean up could be adding this function to a base test contract helper since its in most test files

Copy link
Contributor Author

Choose a reason for hiding this comment

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

for sure!

SafeTX memory safeTX = SafeTX({to: to, value: value, data: data, operation: operation});

uint256 nonce = safe.nonce();

bytes32 txHash = safe.getTransactionHash(
safeTX.to, safeTX.value, safeTX.data, safeTX.operation, 0, 0, 0, address(0), payable(address(0)), nonce
);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, txHash);
bytes memory signature = abi.encodePacked(r, s, v);

safe.execTransaction(
safeTX.to, safeTX.value, safeTX.data, safeTX.operation, 0, 0, 0, address(0), payable(address(0)), signature
);
}
}