Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
ab4a40cc
Unverified
Commit
ab4a40cc
authored
Oct 26, 2023
by
Maurelian
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
safe-tools: Refactor to move free functions into SafeTestLib
parent
3787e706
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
116 additions
and
111 deletions
+116
-111
LivenessGuard.t.sol
packages/contracts-bedrock/test/LivenessGuard.t.sol
+1
-1
LivenessModule.t.sol
packages/contracts-bedrock/test/LivenessModule.t.sol
+1
-1
SafeSigners.t.sol
packages/contracts-bedrock/test/SafeSigners.t.sol
+6
-5
SafeTestTools.sol
packages/contracts-bedrock/test/safe-tools/SafeTestTools.sol
+108
-104
No files found.
packages/contracts-bedrock/test/LivenessGuard.t.sol
View file @
ab4a40cc
...
@@ -137,7 +137,7 @@ contract LivenessGuard_OwnerManagement_Test is LivenessGuard_TestInit {
...
@@ -137,7 +137,7 @@ contract LivenessGuard_OwnerManagement_Test is LivenessGuard_TestInit {
safeInstance.execTransaction({
safeInstance.execTransaction({
to: address(safeInstance.safe),
to: address(safeInstance.safe),
value: 0,
value: 0,
data: abi.encodeWithSelector(OwnerManager.removeOwner.selector, SENTINEL_OWNERS, ownerToRemove, 1)
data: abi.encodeWithSelector(OwnerManager.removeOwner.selector, S
afeTestLib.S
ENTINEL_OWNERS, ownerToRemove, 1)
});
});
assertFalse(safeInstance.safe.isOwner(ownerToRemove));
assertFalse(safeInstance.safe.isOwner(ownerToRemove));
...
...
packages/contracts-bedrock/test/LivenessModule.t.sol
View file @
ab4a40cc
...
@@ -42,7 +42,7 @@ contract LivenessModule_TestInit is Test, SafeTestTools {
...
@@ -42,7 +42,7 @@ contract LivenessModule_TestInit is Test, SafeTestTools {
vm.warp(initTime);
vm.warp(initTime);
// Create a Safe with 10 owners
// Create a Safe with 10 owners
(, uint256[] memory keys) = makeAddrsAndKeys(10);
(, uint256[] memory keys) =
SafeTestLib.
makeAddrsAndKeys(10);
safeInstance = _setupSafe(keys, 8);
safeInstance = _setupSafe(keys, 8);
livenessGuard = new LivenessGuard(safeInstance.safe);
livenessGuard = new LivenessGuard(safeInstance.safe);
...
...
packages/contracts-bedrock/test/SafeSigners.t.sol
View file @
ab4a40cc
...
@@ -39,10 +39,11 @@ contract SafeSigners_Test is Test, SafeTestTools {
...
@@ -39,10 +39,11 @@ contract SafeSigners_Test is Test, SafeTestTools {
// Limit the number of signatures to 25
// Limit the number of signatures to 25
uint256 numSigs = bound(_numSigs, 1, 25);
uint256 numSigs = bound(_numSigs, 1, 25);
(, uint256[] memory keys) = makeAddrsAndKeys(numSigs);
(, uint256[] memory keys) =
SafeTestLib.
makeAddrsAndKeys(numSigs);
for (uint256 i = 0; i < keys.length; i++) {
for (uint256 i = 0; i < keys.length; i++) {
if (sigType(keys[i]) == SigTypes.Contract) {
if (sigType(keys[i]) == SigTypes.Contract) {
keys[i] = encodeSmartContractWalletAsPK(decodeSmartContractWalletAsAddress(keys[i]));
keys[i] =
SafeTestLib.encodeSmartContractWalletAsPK(SafeTestLib.decodeSmartContractWalletAsAddress(keys[i]));
}
}
}
}
...
@@ -66,15 +67,15 @@ contract SafeSigners_Test is Test, SafeTestTools {
...
@@ -66,15 +67,15 @@ contract SafeSigners_Test is Test, SafeTestTools {
v += 4;
v += 4;
signatures = bytes.concat(signatures, abi.encodePacked(r, s, v));
signatures = bytes.concat(signatures, abi.encodePacked(r, s, v));
} else if (sigType(pks[i]) == SigTypes.ApprovedHash) {
} else if (sigType(pks[i]) == SigTypes.ApprovedHash) {
vm.prank(getAddr(pks[i]));
vm.prank(
SafeTestLib.
getAddr(pks[i]));
safeInstance.safe.approveHash(digest);
safeInstance.safe.approveHash(digest);
v = 1;
v = 1;
// s is not checked on approved hash signatures, so we can leave it as zero.
// s is not checked on approved hash signatures, so we can leave it as zero.
r = bytes32(uint256(uint160(getAddr(pks[i]))));
r = bytes32(uint256(uint160(
SafeTestLib.
getAddr(pks[i]))));
signatures = bytes.concat(signatures, abi.encodePacked(r, s, v));
signatures = bytes.concat(signatures, abi.encodePacked(r, s, v));
} else if (sigType(pks[i]) == SigTypes.Contract) {
} else if (sigType(pks[i]) == SigTypes.Contract) {
contractSigs++;
contractSigs++;
address addr = decodeSmartContractWalletAsAddress(pks[i]);
address addr =
SafeTestLib.
decodeSmartContractWalletAsAddress(pks[i]);
r = bytes32(uint256(uint160(addr)));
r = bytes32(uint256(uint160(addr)));
vm.mockCall(
vm.mockCall(
addr, abi.encodeWithSignature("isValidSignature(bytes,bytes)"), abi.encode(EIP1271_MAGIC_VALUE)
addr, abi.encodeWithSignature("isValidSignature(bytes,bytes)"), abi.encode(EIP1271_MAGIC_VALUE)
...
...
packages/contracts-bedrock/test/safe-tools/SafeTestTools.sol
View file @
ab4a40cc
...
@@ -11,104 +11,7 @@ import "./CompatibilityFallbackHandler_1_3_0.sol";
...
@@ -11,104 +11,7 @@ import "./CompatibilityFallbackHandler_1_3_0.sol";
// Tools to simplify testing Safe contracts
// Tools to simplify testing Safe contracts
// Author: Colin Nielsen (https://github.com/colinnielsen/safe-tools)
// Author: Colin Nielsen (https://github.com/colinnielsen/safe-tools)
// With expanded and improved functionality by OP Labs
address constant VM_ADDR = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
bytes12 constant ADDR_MASK = 0xffffffffffffffffffffffff;
/// @dev The address of the first owner in the linked list of owners
address constant SENTINEL_OWNERS = address(0x1);
/// @dev Get the address from a private key
function getAddr(uint256 pk) pure returns (address) {
return Vm(VM_ADDR).addr(pk);
}
/// @dev Get arrays of addresses and private keys. The arrays are sorted by address, and the addresses are labelled
function makeAddrsAndKeys(uint256 num) returns (address[] memory addrs, uint256[] memory keys) {
keys = new uint256[](num);
addrs = new address[](num);
for (uint256 i; i < num; i++) {
uint256 key = uint256(keccak256(abi.encodePacked(i)));
keys[i] = key;
}
for (uint256 i; i < num; i++) {
addrs[i] = Vm(VM_ADDR).addr(keys[i]);
Vm(VM_ADDR).label(getAddr(keys[i]), string.concat("SAFETEST: Signer ", string(abi.encodePacked(bytes32(i)))));
}
}
/// @dev Encode a smart contract wallet as a private key
function encodeSmartContractWalletAsPK(address addr) pure returns (uint256 encodedPK) {
assembly {
let addr_b32 := addr
encodedPK := or(addr, ADDR_MASK)
}
}
/// @dev Decode a smart contract wallet as an address from a private key
function decodeSmartContractWalletAsAddress(uint256 pk) pure returns (address decodedAddr) {
assembly {
let addr := shl(96, pk)
decodedAddr := shr(96, addr)
}
}
/// @dev Checks if a private key is an encoded smart contract address
function isSmartContractPK(uint256 pk) pure returns (bool isEncoded) {
assembly {
isEncoded := eq(shr(160, pk), shr(160, ADDR_MASK))
}
}
library Sort {
/// @dev Sorts an array of addresses in place
function sort(address[] memory arr) public pure returns (address[] memory) {
LibSort.sort(arr);
return arr;
}
}
/// @dev Sorts an array of private keys by the computed address
/// If the private key is a smart contract wallet, it will be decoded and sorted by the address
function sortPKsByComputedAddress(uint256[] memory _pks) pure returns (uint256[] memory) {
uint256[] memory sortedPKs = new uint256[](_pks.length);
address[] memory addresses = new address[](_pks.length);
bytes32[2][] memory accounts = new bytes32[2][](_pks.length);
for (uint256 i; i < _pks.length; i++) {
uint256 pk = _pks[i];
address signer = getAddr(pk);
if (isSmartContractPK(pk)) {
signer = decodeSmartContractWalletAsAddress(pk);
}
addresses[i] = signer;
accounts[i][0] = bytes32(abi.encode(signer));
accounts[i][1] = bytes32(pk);
}
addresses = Sort.sort(addresses);
uint256 found;
for (uint256 j; j < addresses.length; j++) {
address signer = addresses[j];
uint256 pk;
for (uint256 k; k < accounts.length; k++) {
if (address(uint160(uint256(accounts[k][0]))) == signer) {
pk = uint256(accounts[k][1]);
found++;
}
}
sortedPKs[j] = pk;
}
if (found < _pks.length) {
revert("SAFETESTTOOLS: issue with private key sorting, please open a ticket on github");
}
return sortedPKs;
}
/// @dev A minimal wrapper around the OwnerManager contract. This contract is meant to be initialized with
/// @dev A minimal wrapper around the OwnerManager contract. This contract is meant to be initialized with
/// the same owners as a Safe instance, and then used to simulate the resulting owners list
/// the same owners as a Safe instance, and then used to simulate the resulting owners list
...
@@ -146,7 +49,108 @@ struct SafeInstance {
...
@@ -146,7 +49,108 @@ struct SafeInstance {
DeployedSafe safe;
DeployedSafe safe;
}
}
library Sort {
/// @dev Sorts an array of addresses in place
function sort(address[] memory arr) public pure returns (address[] memory) {
LibSort.sort(arr);
return arr;
}
}
library SafeTestLib {
library SafeTestLib {
/// @dev The address of foundry's VM contract
address constant VM_ADDR = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
/// @dev The address of the first owner in the linked list of owners
address constant SENTINEL_OWNERS = address(0x1);
/// @dev Get the address from a private key
function getAddr(uint256 pk) internal pure returns (address) {
return Vm(VM_ADDR).addr(pk);
}
/// @dev Get arrays of addresses and private keys. The arrays are sorted by address, and the addresses are labelled
function makeAddrsAndKeys(uint256 num) internal returns (address[] memory addrs, uint256[] memory keys) {
keys = new uint256[](num);
addrs = new address[](num);
for (uint256 i; i < num; i++) {
uint256 key = uint256(keccak256(abi.encodePacked(i)));
keys[i] = key;
}
for (uint256 i; i < num; i++) {
addrs[i] = Vm(VM_ADDR).addr(keys[i]);
Vm(VM_ADDR).label(
getAddr(keys[i]), string.concat("SAFETEST: Signer ", string(abi.encodePacked(bytes32(i))))
);
}
}
bytes12 constant ADDR_MASK = 0xffffffffffffffffffffffff;
/// @dev Encode a smart contract wallet as a private key
function encodeSmartContractWalletAsPK(address addr) internal pure returns (uint256 encodedPK) {
assembly {
let addr_b32 := addr
encodedPK := or(addr, ADDR_MASK)
}
}
/// @dev Decode a smart contract wallet as an address from a private key
function decodeSmartContractWalletAsAddress(uint256 pk) internal pure returns (address decodedAddr) {
assembly {
let addr := shl(96, pk)
decodedAddr := shr(96, addr)
}
}
/// @dev Checks if a private key is an encoded smart contract address
function isSmartContractPK(uint256 pk) internal pure returns (bool isEncoded) {
assembly {
isEncoded := eq(shr(160, pk), shr(160, ADDR_MASK))
}
}
/// @dev Sorts an array of private keys by the computed address
/// If the private key is a smart contract wallet, it will be decoded and sorted by the address
function sortPKsByComputedAddress(uint256[] memory _pks) internal pure returns (uint256[] memory) {
uint256[] memory sortedPKs = new uint256[](_pks.length);
address[] memory addresses = new address[](_pks.length);
bytes32[2][] memory accounts = new bytes32[2][](_pks.length);
for (uint256 i; i < _pks.length; i++) {
uint256 pk = _pks[i];
address signer = SafeTestLib.getAddr(pk);
if (isSmartContractPK(pk)) {
signer = decodeSmartContractWalletAsAddress(pk);
}
addresses[i] = signer;
accounts[i][0] = bytes32(abi.encode(signer));
accounts[i][1] = bytes32(pk);
}
addresses = Sort.sort(addresses);
uint256 found;
for (uint256 j; j < addresses.length; j++) {
address signer = addresses[j];
uint256 pk;
for (uint256 k; k < accounts.length; k++) {
if (address(uint160(uint256(accounts[k][0]))) == signer) {
pk = uint256(accounts[k][1]);
found++;
}
}
sortedPKs[j] = pk;
}
if (found < _pks.length) {
revert("SAFETESTTOOLS: issue with private key sorting, please open a ticket on github");
}
return sortedPKs;
}
/// @dev A wrapper for the full execTransaction method, if no signatures are provided it will
/// @dev A wrapper for the full execTransaction method, if no signatures are provided it will
/// generate them for all owners.
/// generate them for all owners.
function execTransaction(
function execTransaction(
...
@@ -442,14 +446,14 @@ contract SafeTestTools {
...
@@ -442,14 +446,14 @@ contract SafeTestTools {
public
public
returns (SafeInstance memory)
returns (SafeInstance memory)
{
{
uint256[] memory sortedPKs = sortPKsByComputedAddress(ownerPKs);
uint256[] memory sortedPKs =
SafeTestLib.
sortPKsByComputedAddress(ownerPKs);
address[] memory owners = new address[](sortedPKs.length);
address[] memory owners = new address[](sortedPKs.length);
for (uint256 i; i < sortedPKs.length; i++) {
for (uint256 i; i < sortedPKs.length; i++) {
if (isSmartContractPK(sortedPKs[i])) {
if (
SafeTestLib.
isSmartContractPK(sortedPKs[i])) {
owners[i] = decodeSmartContractWalletAsAddress(sortedPKs[i]);
owners[i] =
SafeTestLib.
decodeSmartContractWalletAsAddress(sortedPKs[i]);
} else {
} else {
owners[i] = getAddr(sortedPKs[i]);
owners[i] =
SafeTestLib.
getAddr(sortedPKs[i]);
}
}
}
}
// store the initialization parameters
// store the initialization parameters
...
@@ -482,7 +486,7 @@ contract SafeTestTools {
...
@@ -482,7 +486,7 @@ contract SafeTestTools {
});
});
instances.push(instance0);
instances.push(instance0);
Vm(VM_ADDR).deal(address(safe0), initialBalance);
Vm(
SafeTestLib.
VM_ADDR).deal(address(safe0), initialBalance);
return instance0;
return instance0;
}
}
...
@@ -531,7 +535,7 @@ contract SafeTestTools {
...
@@ -531,7 +535,7 @@ contract SafeTestTools {
}
}
function _setupSafe() public returns (SafeInstance memory) {
function _setupSafe() public returns (SafeInstance memory) {
(, uint256[] memory defaultPKs) = makeAddrsAndKeys(3);
(, uint256[] memory defaultPKs) =
SafeTestLib.
makeAddrsAndKeys(3);
return _setupSafe(
return _setupSafe(
defaultPKs,
defaultPKs,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment