Skip to content
This repository has been archived by the owner on Jun 27, 2024. It is now read-only.

Commit

Permalink
🏗️ Deploy tests as in production
Browse files Browse the repository at this point in the history
  • Loading branch information
ernestognw committed Dec 11, 2023
1 parent 13a1ade commit d07f51e
Show file tree
Hide file tree
Showing 6 changed files with 461 additions and 201 deletions.
56 changes: 28 additions & 28 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
PlumaaTest:test_GivenAExpiredRequest(uint48) (runs: 256, μ: 97507, ~: 97507)
PlumaaTest:test_GivenANonSafeOwner(address,bytes,bytes) (runs: 256, μ: 23732, ~: 23706)
PlumaaTest:test_GivenATamperedExecutingData(bytes) (runs: 256, μ: 193968, ~: 193844)
PlumaaTest:test_GivenATamperedExecutingDeadline(uint48) (runs: 256, μ: 192766, ~: 192765)
PlumaaTest:test_GivenATamperedExecutingOperation() (gas: 192159)
PlumaaTest:test_GivenATamperedExecutingSignature(bytes) (runs: 256, μ: 140056, ~: 139848)
PlumaaTest:test_GivenATamperedExecutingTo(address) (runs: 256, μ: 192797, ~: 192797)
PlumaaTest:test_GivenATamperedExecutingValue(uint256) (runs: 256, μ: 192723, ~: 192722)
PlumaaTest:test_GivenATamperedVerifyingData(bytes) (runs: 256, μ: 160541, ~: 160473)
PlumaaTest:test_GivenATamperedVerifyingDeadline(uint48) (runs: 256, μ: 159423, ~: 159422)
PlumaaTest:test_GivenATamperedVerifyingOperation() (gas: 158853)
PlumaaTest:test_GivenATamperedVerifyingSignature(bytes) (runs: 256, μ: 113445, ~: 113337)
PlumaaTest:test_GivenATamperedVerifyingTo(address) (runs: 256, μ: 159466, ~: 159466)
PlumaaTest:test_GivenATamperedVerifyingValue(uint256) (runs: 256, μ: 159366, ~: 159365)
PlumaaTest:test_GivenAValidExecutingOwner(uint256,bytes,uint48,uint32) (runs: 256, μ: 250355, ~: 251916)
PlumaaTest:test_GivenAValidVerifyingOwner(address,uint256,uint48,uint8,bytes,uint32) (runs: 256, μ: 164829, ~: 164917)
PlumaaTest:test_GivenAnInvalidExecutingNonce(uint32) (runs: 256, μ: 192388, ~: 192386)
PlumaaTest:test_GivenAnInvalidExecutingOwner() (gas: 132638)
PlumaaTest:test_GivenAnInvalidVerifyingNonce(uint32) (runs: 256, μ: 159337, ~: 159335)
PlumaaTest:test_GivenAnInvalidVerifyingOwner(address,uint256,uint48,uint8,bytes,uint32) (runs: 256, μ: 99349, ~: 99437)
PlumaaTest:test_GivenTheSafeOwner(bytes,bytes) (runs: 256, μ: 46081, ~: 45906)
PlumaaTest:test_WhenInitialized() (gas: 47676)
RSAOwnerManagerTest:test_GivenASignatureFromANonOwner(bytes) (runs: 256, μ: 77780, ~: 77506)
RSAOwnerManagerTest:test_GivenASignatureFromTheOwner(bytes) (runs: 256, μ: 143403, ~: 143130)
RSAOwnerManagerTest:test_GivenAnInvalidSignature(bytes) (runs: 256, μ: 55406, ~: 55295)
PlumaaTest:test_GivenAExpiredRequest(uint48) (runs: 256, μ: 101028, ~: 101028)
PlumaaTest:test_GivenANonSafeOwner(address,bytes,bytes) (runs: 256, μ: 28910, ~: 28884)
PlumaaTest:test_GivenATamperedExecutingData(bytes) (runs: 256, μ: 198003, ~: 197880)
PlumaaTest:test_GivenATamperedExecutingDeadline(uint48) (runs: 256, μ: 196801, ~: 196801)
PlumaaTest:test_GivenATamperedExecutingOperation() (gas: 196195)
PlumaaTest:test_GivenATamperedExecutingSignature(bytes) (runs: 256, μ: 143577, ~: 143369)
PlumaaTest:test_GivenATamperedExecutingTo(address) (runs: 256, μ: 196833, ~: 196833)
PlumaaTest:test_GivenATamperedExecutingValue(uint256) (runs: 256, μ: 196760, ~: 196758)
PlumaaTest:test_GivenATamperedVerifyingData(bytes) (runs: 256, μ: 164060, ~: 163994)
PlumaaTest:test_GivenATamperedVerifyingDeadline(uint48) (runs: 256, μ: 162943, ~: 162943)
PlumaaTest:test_GivenATamperedVerifyingOperation() (gas: 162374)
PlumaaTest:test_GivenATamperedVerifyingSignature(bytes) (runs: 256, μ: 116966, ~: 116858)
PlumaaTest:test_GivenATamperedVerifyingTo(address) (runs: 256, μ: 162987, ~: 162987)
PlumaaTest:test_GivenATamperedVerifyingValue(uint256) (runs: 256, μ: 162888, ~: 162886)
PlumaaTest:test_GivenAValidExecutingOwner(uint256,bytes,uint48,uint32) (runs: 256, μ: 254899, ~: 256461)
PlumaaTest:test_GivenAValidVerifyingOwner(address,uint256,uint48,uint8,bytes,uint32) (runs: 256, μ: 168368, ~: 168438)
PlumaaTest:test_GivenAnInvalidExecutingNonce(uint32) (runs: 256, μ: 196422, ~: 196422)
PlumaaTest:test_GivenAnInvalidExecutingOwner() (gas: 136159)
PlumaaTest:test_GivenAnInvalidVerifyingNonce(uint32) (runs: 256, μ: 162856, ~: 162856)
PlumaaTest:test_GivenAnInvalidVerifyingOwner(address,uint256,uint48,uint8,bytes,uint32) (runs: 256, μ: 102888, ~: 102958)
PlumaaTest:test_GivenTheSafeOwner(bytes,bytes) (runs: 256, μ: 50087, ~: 49912)
PlumaaTest:test_WhenInitialized() (gas: 51200)
RSAOwnerManagerTest:test_GivenASignatureFromANonOwner(bytes) (runs: 256, μ: 77796, ~: 77522)
RSAOwnerManagerTest:test_GivenASignatureFromTheOwner(bytes) (runs: 256, μ: 143419, ~: 143146)
RSAOwnerManagerTest:test_GivenAnInvalidSignature(bytes) (runs: 256, μ: 55418, ~: 55307)
RSAOwnerManagerTest:test_WhenCalling_setOwner(bytes,bytes,address) (runs: 256, μ: 22346, ~: 22271)
RSAOwnerManagerTest:test_WhenCalling_useOwnerNonce(uint32) (runs: 256, μ: 35579, ~: 35579)
RSAOwnerManagerTest:test_WhenInitialized() (gas: 45142)
SafeManagerTests:test_GivenACallFromOtherThanTheSafe(address) (runs: 256, μ: 16464, ~: 16464)
SafeManagerTests:test_GivenACallFromTheSafe() (gas: 27651)
RSAOwnerManagerTest:test_WhenInitialized() (gas: 45150)
SafeManagerTests:test_GivenACallFromOtherThanTheSafe(address) (runs: 256, μ: 16460, ~: 16460)
SafeManagerTests:test_GivenACallFromTheSafe() (gas: 27645)
SafeManagerTests:test_WhenInitialized() (gas: 12307)
89 changes: 57 additions & 32 deletions src/PlumaaFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,75 @@
pragma solidity ^0.8.20;

import {Safe} from "@safe/contracts/Safe.sol";
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import {Plumaa} from "./Plumaa.sol";

contract PlumaaFactory {
using Address for address;
using Clones for address;
address private immutable __self;

address immutable singletonProxy;

/// @notice Creates a new PlumaaFactory contract.
constructor(address _singletonProxy) {
singletonProxy = _singletonProxy;
}

/// @notice Creates a new Plumaa contract.
function create(bytes memory exponent, bytes memory modulus) public returns (address) {
address clone = Clones.clone(singletonProxy);
Plumaa(clone).setupPlumaa(exponent, modulus, Safe(payable(msg.sender)));
return clone;
/// @notice Modifier to make a function callable via delegatecall only.
/// If the function is called via a regular call, it will revert.
modifier onlyDelegateCall() {
require(address(this) != __self);
_;
}

/// @notice Creates a new Plumaa contract in a deterministic way
function createDeterministic(bytes32 salt, bytes memory exponent, bytes memory modulus) public returns (address) {
address clone = Clones.cloneDeterministic(singletonProxy, _toSafeSalt(salt));
Plumaa(clone).setupPlumaa(exponent, modulus, Safe(payable(msg.sender)));
return clone;
constructor() {
__self = address(this);
}

/// @notice Predicts the address of a new Plumaa contract
function predictDeterministicAddress(bytes32 salt) public view returns (address) {
return singletonProxy.predictDeterministicAddress(_toSafeSalt(salt), address(this));
function predictDeterministicAddress(
address beacon,
bytes32 salt,
bytes memory exponent,
bytes memory modulus,
Safe safe
) public pure returns (address) {
return
Create2.computeAddress(
salt,
keccak256(_proxyCreationCode(beacon, exponent, modulus, safe)),
address(safe)
);
}

/// @notice Optionally calls target with `data` if larger than a Solidity
/// selector (4 bytes).
function _optionalCall(address target, bytes memory data) internal {
if (data.length >= 4) {
target.functionCall(data);
}
/// @notice Creates a new Plumaa contract and enables it as a module in a Safe.
/// This method can only be called as a delegatecall from a Safe. This is possible through
/// the Safe's {setup} method which includes optional delegatecall data.
function safeSetup(
address beacon,
bytes32 salt,
bytes memory exponent,
bytes memory modulus
) public onlyDelegateCall {
Safe safe = Safe(payable(address(this)));
address clone = Create2.deploy(
0,
salt,
_proxyCreationCode(beacon, exponent, modulus, safe)
);
safe.enableModule(clone);
}

/// @notice Creates a salt scoped to each `msg.sender`.
function _toSafeSalt(bytes32 salt) internal view returns (bytes32) {
return keccak256(abi.encodePacked(salt, msg.sender));
/// @notice Returns the creation code with arguments of a new beacon proxy.
function _proxyCreationCode(
address beacon,
bytes memory exponent,
bytes memory modulus,
Safe safe
) internal pure returns (bytes memory) {
return
abi.encodePacked(
type(BeaconProxy).creationCode,
abi.encode(
beacon,
abi.encodeCall(
Plumaa.setupPlumaa,
(exponent, modulus, safe)
)
)
);
}
}
4 changes: 1 addition & 3 deletions src/base/RSAOwnerManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ abstract contract RSAOwnerManager is Initializable {
/// @notice Emitted when the owner is changed.
event OwnershipTransferred(bytes32 indexed previousOwner, bytes32 indexed newOwner);

// keccak256(abi.encode(uint256(keccak256("plumaa.storage.RSAOwnerManager"))
// - 1)) &
// ~bytes32(uint256(0xff))
// keccak256(abi.encode(uint256(keccak256("plumaa.storage.RSAOwnerManager")) - 1)) & ~bytes32(uint256(0xff))
bytes32 constant RSAOwnerManagerStorageLocation = 0xd2cca958b80dbad5ce6e876a8c46f66173a169ce6aba515198c38d288b5cc600;

struct RSAOwnerManagerStorage {
Expand Down
4 changes: 1 addition & 3 deletions src/base/SafeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ contract SafeManager is Initializable {
/// @dev The caller account is not authorized to perform an operation.
error SafeManagerUnauthorizedAccount(address account);

// keccak256(abi.encode(uint256(keccak256("plumaa.storage.SafeManager")) -
// 1)) &
// ~bytes32(uint256(0xff))
// keccak256(abi.encode(uint256(keccak256("plumaa.storage.SafeManager")) - 1)) & ~bytes32(uint256(0xff))
bytes32 constant SafeManagerStorageLocation = 0x6465d3aa1a400bf0e2554af5439f7f8a5b30fd78a22df4e95de86b9d82986200;

struct SafeManagerStorage {
Expand Down
112 changes: 82 additions & 30 deletions test/Base.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {ModuleManager} from "@safe/contracts/base/ModuleManager.sol";
import {TransactionRequest} from "./mocks/Plumaa.m.sol";
import {PlumaaFactory} from "~/PlumaaFactory.sol";
import {PlumaaMock} from "./mocks/Plumaa.m.sol";
import {Safe, SafeMock} from "./mocks/Safe.m.sol";
import {Enum} from "@safe/contracts/common/Enum.sol";
Expand All @@ -13,41 +15,31 @@ import {Upgrades} from "@openzeppelin/foundry-upgrades/Upgrades.sol";
import {RSASigner} from "./utils/RSASigner.sol";

contract BaseTest is Test {
// Signers
RSASigner internal owner;
RSASigner internal other;

/// Factories
PlumaaFactory plumaaFactory;
SafeProxyFactory safeProxyFactory;

// Mocks
PlumaaMock internal plumaa;
SafeMock internal safe;
address internal receiver = address(0x1234);
address proxyAdmin;

uint256 internal callerPrivateKey;
address internal caller;

modifier asCaller() {
vm.startPrank(caller);
_;
vm.stopPrank();
}

function setUp() public virtual {
owner = new RSASigner("owner");
other = new RSASigner("other");

RSASigner.PublicKey memory publicKey = owner.publicKey();
plumaaFactory = new PlumaaFactory();
safeProxyFactory = new SafeProxyFactory();

safe = _deploySafeMock();
address _proxy = Upgrades.deployTransparentProxy(
address plumaaBeacon = Upgrades.deployBeacon(
"Plumaa.m.sol:PlumaaMock",
address(this),
abi.encodeCall(Plumaa.setupPlumaa, (publicKey.exponent, publicKey.modulus, safe))
address(this)
);
plumaa = PlumaaMock(_proxy);
_forceEnableModule(address(plumaa));
proxyAdmin = computeCreateAddress(address(_proxy), 1);

callerPrivateKey = 0xA11CE;
caller = vm.addr(callerPrivateKey);
(safe, plumaa) = _deployMocks(address(new SafeMock()), plumaaBeacon);
}

function _forgeRequestData(
Expand All @@ -58,8 +50,18 @@ contract BaseTest is Test {
uint48 deadline,
bytes memory data,
uint32 nonce
) internal returns (Plumaa.TransactionRequestData memory, bytes32 structHash) {
structHash = _requestStructHash(to, value, operation, deadline, data, nonce);
)
internal
returns (Plumaa.TransactionRequestData memory, bytes32 structHash)
{
structHash = _requestStructHash(
to,
value,
operation,
deadline,
data,
nonce
);
bytes memory signature = signer.sign(abi.encodePacked(structHash));

RSASigner.PublicKey memory publicKey = signer.publicKey();
Expand Down Expand Up @@ -99,22 +101,72 @@ contract BaseTest is Test {
return plumaa.structHash(request);
}

function _deploySafeMock() private returns (SafeMock) {
SafeProxyFactory factory = new SafeProxyFactory();
function _deployMocks(
address safeSingleton,
address plumaaBeacon
) private returns (SafeMock, PlumaaMock) {
bytes32 salt = keccak256("salt");
address singleton = address(new SafeMock());

RSASigner.PublicKey memory publicKey = owner.publicKey();
address payable safeProxy = payable(
safeProxyFactory.createProxyWithNonce(
safeSingleton,
_buildDeploySafeMockData(plumaaBeacon, salt, publicKey),
uint256(salt)
)
);

address plumaaAddress = plumaaFactory.predictDeterministicAddress(
plumaaBeacon,
salt,
publicKey.exponent,
publicKey.modulus,
Safe(safeProxy)
);

return (SafeMock(safeProxy), PlumaaMock(plumaaAddress));
}

function _buildDeploySafeMockData(
address plumaaBeacon,
bytes32 salt,
RSASigner.PublicKey memory publicKey
) private view returns (bytes memory) {
address[] memory owners = new address[](1);
owners[0] = address(this);
bytes memory data =
abi.encodeCall(Safe.setup, (owners, 1, address(0), "", address(0), address(0), 0, payable(address(0))));

return SafeMock(payable(address(factory.createProxyWithNonce(singleton, data, uint256(salt)))));
return
abi.encodeCall(
Safe.setup,
(
owners,
1,
address(plumaaFactory),
abi.encodeCall(
PlumaaFactory.safeSetup,
(
plumaaBeacon,
salt,
publicKey.exponent,
publicKey.modulus
)
),
address(0),
address(0),
0,
payable(0)
)
);
}

function _forceEnableModule(address module) internal {
// Enable as a module to bypass signatures
// https://twitter.com/0xVazi/status/1732187067776696655
vm.store(address(safe), keccak256(abi.encode(address(module), 1)), bytes32(uint256(1)));
vm.store(
address(safe),
keccak256(abi.encode(address(module), 1)),
bytes32(uint256(1))
);
assertTrue(safe.isModuleEnabled(address(module)));
}
}
Loading

0 comments on commit d07f51e

Please sign in to comment.