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

Implement new factory #106

Draft
wants to merge 31 commits into
base: handle-contract-versions
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3e9541f
Implement new factory
cag Sep 10, 2020
680418d
Use canonical Safe factory with locked down implementation setter idea
cag Nov 26, 2020
18174ba
At least mention the intention of a block of assembly
cag Nov 26, 2020
744e621
Add deleted import from rebase
cag Dec 23, 2020
f983fb1
Switch to solc 0.5.x for now
cag Dec 23, 2020
c03be8f
Punt compiler version issues
cag Dec 23, 2020
cf8db27
Add proxy runtime code accessor
cag Dec 23, 2020
fd99174
Add proxy EXTCODEHASH accessor to factory
cag Dec 23, 2020
424dcc3
Remove duplicate ProxyCreation
cag Dec 23, 2020
8922ccd
Emit CPKCreation event on proxy creation
cag Dec 23, 2020
f33f37e
Add a version number accessor on CPKFactory
cag Dec 23, 2020
ded5d3a
Fix CPKFactory to not depend on Safe version for proxy address determ…
cag Dec 23, 2020
5723e7f
Fix ProxyImplSetter compiler error
cag Dec 23, 2020
dc52c6a
Enable optimizer and remove code which produces non-canonical bytecode
cag Dec 23, 2020
296dd55
Rename saltNonce to saltNonceSalt
cag Dec 23, 2020
400bff9
Load owner from the end of signature data and comment more
cag Dec 23, 2020
9a9a574
Rename saltNonceSalt to salt
cag Dec 23, 2020
0561f4f
Port to solc 0.8.x
cag Dec 28, 2020
dae3b15
Generalize proxy setup and move version-specific calldata into facade
cag Dec 28, 2020
ddf8029
Allow tx value to be specified for each tx submitted to factory in array
cag Dec 28, 2020
32d2b8f
Add immutable to properties
cag Dec 28, 2020
196dd9b
Fix missing/wrong symbols
cag Dec 29, 2020
b2e18c0
More silly errors and warnings fixed
cag Dec 30, 2020
4368c56
Found an approach that might work
cag Dec 30, 2020
365cbe4
Upgraded package.json build tasks for multi-compiler support
cag Dec 30, 2020
40d3be3
Made migrations work
cag Dec 30, 2020
b504a00
Increase testing timeout
cag Dec 30, 2020
e65719d
Rename to reflect that multiple txs executed when creating proxy
cag Dec 31, 2020
63e5bc2
Workaround for 0.5 compilation settings wrongly targeting 0.8 contracts
cag Dec 31, 2020
4f849c1
Adapt TS to work with new contracts
cag Jan 6, 2021
d92fc7f
Fix tests to reflect potential real config with V1 factory as well
cag Jan 6, 2021
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
31 changes: 0 additions & 31 deletions contracts/Migrations.sol

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol";
import { GnosisSafeProxy } from "@gnosis.pm/safe-contracts/contracts/proxies/GnosisSafeProxy.sol";
import { GnosisSafe } from "@gnosis.pm/safe-contracts/contracts/GnosisSafe.sol";

contract CPKFactory {
contract CPKFactoryV1 {
event ProxyCreation(GnosisSafeProxy proxy);

function proxyCreationCode() external pure returns (bytes memory) {
Expand Down
File renamed without changes.
109 changes: 109 additions & 0 deletions contracts/solc-0.8/CPKFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;

import { IGnosisSafeProxyFactory } from "./dep-ports/IGnosisSafeProxyFactory.sol";
import { ProxyImplSetter } from "./ProxyImplSetter.sol";
import { SafeSignatureUtils } from "./SafeSignatureUtils.sol";

enum TxReaction {
RevertOnReturnFalse,
CaptureBoolReturn,
IgnoreReturn
}

struct CPKFactoryTx {
uint value;
bytes data;
TxReaction reaction;
}

contract CPKFactory {
using SafeSignatureUtils for bytes;

event CPKCreation(
address indexed proxy,
address initialImpl,
address initialOwner,
uint256 salt
);

uint256 public constant version = 2;
ProxyImplSetter public immutable proxyImplSetter;
IGnosisSafeProxyFactory public immutable gnosisSafeProxyFactory;
bytes32 public immutable proxyExtCodeHash;

constructor(IGnosisSafeProxyFactory _gnosisSafeProxyFactory) {
proxyImplSetter = new ProxyImplSetter(address(this));
gnosisSafeProxyFactory = _gnosisSafeProxyFactory;
proxyExtCodeHash = keccak256(_gnosisSafeProxyFactory.proxyRuntimeCode());
}

function proxyCreationCode() external view returns (bytes memory) {
return gnosisSafeProxyFactory.proxyCreationCode();
}

function proxyRuntimeCode() external view returns (bytes memory) {
return gnosisSafeProxyFactory.proxyRuntimeCode();
}

function createProxyAndExecTransactions(
address owner,
address safeVersion,
uint256 salt,
CPKFactoryTx[] calldata txs,
bytes calldata signature
)
external
payable
returns (bool execTransactionSuccess)
{
bytes memory data = abi.encode(safeVersion, salt, txs);
bytes32 dataHash = keccak256(data);
signature.check(dataHash, data, owner);

bytes32 saltNonce = keccak256(abi.encode(owner, salt));

address payable proxy = gnosisSafeProxyFactory.createProxyWithNonce(
address(proxyImplSetter),
"",
uint256(saltNonce)
);

ProxyImplSetter(proxy).setImplementation(safeVersion);

uint sumTxsValues = 0;
for (uint i = 0; i < txs.length; i++) {
bool txSuccess;
bytes memory returnData;
uint txValue = txs[i].value;
sumTxsValues += txValue;
(txSuccess, returnData) = proxy.call{value: txValue}(txs[i].data);
assembly {
// txSuccess == 0 means the call failed
if iszero(txSuccess) {
// The revert data begins one word after the returnData pointer.
// At the location returnData in memory, the length of the bytes is stored.
// This differs from the high-level revert(string(returnData))
// as the high-level version encodes the returnData in a Error(string) object.
// We want to avoid that because the underlying call should have already
// formatted the data in an Error(string) object
revert(add(0x20, returnData), mload(returnData))
}
}

TxReaction txReaction = txs[i].reaction;
if (txReaction == TxReaction.RevertOnReturnFalse) {
bool success = abi.decode(returnData, (bool));
require(success, "tx returned boolean indicating internal failure");
} else if (txReaction == TxReaction.CaptureBoolReturn) {
execTransactionSuccess = abi.decode(returnData, (bool));
} // else txReaction assumed to be IgnoreReturn, which does nothing else here
}

// it is up to the caller to make sure that the msg.value of this method
// equals the sum of all the values in the txs
require(msg.value == sumTxsValues, "msg.value must equal sum of txs' values");

emit CPKCreation(proxy, safeVersion, owner, salt);
}
}
148 changes: 148 additions & 0 deletions contracts/solc-0.8/CPKFactoryFacade.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;

import { Enum } from "./dep-ports/Enum.sol";
import { CPKFactory, CPKFactoryTx, TxReaction } from "./CPKFactory.sol";
import { SafeSignatureUtils } from "./SafeSignatureUtils.sol";

contract CPKFactoryFacade {
using SafeSignatureUtils for bytes;

CPKFactory immutable cpkFactory;
address immutable safeVersion;
uint256 immutable salt;
address immutable fallbackHandler;

constructor(
CPKFactory _cpkFactory,
address _safeVersion,
uint256 _salt,
address _fallbackHandler
) {
cpkFactory = _cpkFactory;
safeVersion = _safeVersion;
salt = _salt;
fallbackHandler = _fallbackHandler;
}

function execTransaction(
address /* to */,
uint256 /* value */,
bytes calldata /* data */,
Enum.Operation /* operation */,
uint256 /* safeTxGas */,
uint256 /* baseGas */,
uint256 /* gasPrice */,
address /* gasToken */,
address payable /* refundReceiver */,
bytes calldata signatures
)
external
payable
returns (bool)
{
// The signatures here aren't just the Gnosis Safe signatures,
// but more data has been appended to them. In particular, the
// format for the signatures is now like the following:
// [inner sig][owner][outer sig]
// where the inner signature is what is submitted to the
// proxy as a first transaction to be executed after its creation,
// the owner is the address of the owner of the new proxy,
// left-padded to 32 bytes, and the outer signature is the overall
// signature required by the CPKFactory.
// The following process copies this signatures data into memory,
// and then figures out the length of the inner signature,
// and then extracts the owner from the signatures in memory,
// and then overwrites the owner in memory with the length
// of the outer signature, which is just the remaining
// bytes of the signature. We end up with a situation in memory like
// [inner sig]*[outer sig length][outer sig]
// where the * is the pointer assigned to outerSig.
bytes memory outerSig;
address owner;
uint innerSigLen;
{
bytes memory innerSig = signatures;
innerSigLen = innerSig.actualLength();
uint outerSigLen = signatures.length - innerSigLen - 0x20;
assembly {
outerSig := add(add(innerSig, 0x20), innerSigLen)
owner := mload(outerSig)
mstore(outerSig, outerSigLen)
}
}

CPKFactoryTx[] memory txs = new CPKFactoryTx[](2);
{
address[] memory owners = new address[](1);
owners[0] = address(owner);
txs[0] = CPKFactoryTx({
value: 0,
data: abi.encodeWithSignature(
"setup("
"address[]," // owners
"uint256," // threshold
"address," // to
"bytes," // data
"address," // fallbackHandler
"address," // paymentToken
"uint256," // payment
"address" // paymentReceiver
")",
owners, uint256(1), address(0), "",
fallbackHandler, address(0), uint256(0), payable(0)
),
reaction: TxReaction.IgnoreReturn
});
}

{
// trying to reencode the msg.data with the params except instead
// of signatures using innerSig would be the obvious and readable approach
// except we run into stack too deep errors that can't be circumvented yet,
// so instead we copy the entire msg.data into memory and
// mutate the signatures portion in it.
bytes memory innerTxData = msg.data;
assembly {
// index param 9 is signatures, and 9 * 0x20 + 4 = 0x124
// 0x124 from the start of the data portion of innerTxData
// contains the offset to the start of the word
// containing a further offset of where the signatures data
// begins.
// We add 0x20 to account for the word containing the length
// of innerTxData, making a total offset of 0x144 to the offset data
// Then we add that offset, a word for the overall length, and
// the pointer to innerTxData, to arrive at a pointer to the signatures
// inside innerTxData
let sigs := add(innerTxData, add(mload(add(innerTxData, 0x144)), 0x20))
// Since we know the length of the inner signature, we get the
// size reduction we need by subtracting the inner signature length
// from the size of all the signature data
let sizeReduction := sub(mload(sigs), innerSigLen)
mstore(sigs, innerSigLen)
// The size reduction is then used to cut the outer signature out
// of the msg.data.
// This assumes that the signatures data is the last chunk of data
// in the msg.data.
// We can't keep the outer signature in the inner transaction data
// because the outer signature signs the inner transaction data,
// so keeping that there would make the outer signature impossible
// to generate.
mstore(innerTxData, sub(mload(innerTxData), sizeReduction))
}
txs[1] = CPKFactoryTx({
value: msg.value,
data: innerTxData,
reaction: TxReaction.CaptureBoolReturn
});
}

return cpkFactory.createProxyAndExecTransactions{value: msg.value}(
owner,
safeVersion,
salt,
txs,
outerSig
);
}
}
7 changes: 4 additions & 3 deletions contracts/Multistep.sol → contracts/solc-0.8/Multistep.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pragma solidity >=0.5.0 <0.7.0;
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;

import { IERC20 } from "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
import { IERC20 } from "./dep-ports/IERC20.sol";

contract Multistep {
event ExpensiveHashMade(bytes32 h);
Expand Down Expand Up @@ -35,7 +36,7 @@ contract Multistep {
require(lastStepFinished[msg.sender] + 1 == step, "must do the next ether step");
require(msg.value >= step * 1 ether, "must provide right amount of ether");
lastStepFinished[msg.sender]++;
msg.sender.transfer(msg.value - step * 1 ether);
payable(msg.sender).transfer(msg.value - step * 1 ether);
makeExpensiveHash(step);
}

Expand Down
17 changes: 17 additions & 0 deletions contracts/solc-0.8/ProxyImplSetter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;

contract ProxyImplSetter {
address public immutable initialSetter;

address implementation;

constructor(address _initialSetter) {
initialSetter = _initialSetter;
}

function setImplementation(address _implementation) external {
require(msg.sender == initialSetter, "Implementation must be set by designated initial setter");
implementation = _implementation;
}
}
Loading