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

Submodule Structure for Forks #149

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ cache
coverage
typechain
dist
forks
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "passport"]
path = passport
url = https://github.com/immutable/wallet-contracts
[submodule "forks/seaport"]
path = forks/seaport
url = https://github.com/immutable/seaport
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ artifacts
cache
coverage*
gasReporterOutput.json

3 changes: 3 additions & 0 deletions .solhintignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
node_modules
contracts/passport
contracts/seaport
forks
59 changes: 59 additions & 0 deletions contracts/passport/Factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.17;
import '@openzeppelin/contracts/access/AccessControl.sol';
import "./Wallet.sol";

/**
* @title Factory
* @notice Factory contract to retrieve counterfactual wallet addresses and
* deploy new Sequence wallet instances to those addresses
*/
contract Factory is AccessControl {
// Role to deploy new wallets
bytes32 public constant DEPLOYER_ROLE = keccak256('DEPLOYER_ROLE');

event WalletDeployed(address indexed wallet, address indexed mainModule, bytes32 salt);

constructor(address _admin, address _deployer) {
_grantRole(DEFAULT_ADMIN_ROLE, _admin);
_grantRole(DEPLOYER_ROLE, _deployer);
}

/**
* @notice Returns a deterministic contract address given a salt
* @param _mainModule Address of the main module to be used by the wallet
* @param _salt Salt used to generate the address
* @return _address The deterministic address
*/
function getAddress(address _mainModule, bytes32 _salt) external view returns (address _address) {
bytes32 _hash = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
_salt,
keccak256(abi.encodePacked(Wallet.creationCode, uint256(uint160(_mainModule))))
)
);
return address(uint160(uint256(_hash)));
}

/**
* @notice Will deploy a new wallet instance using create2
* @param _mainModule Address of the main module to be used by the wallet
* @param _salt Salt used to generate the wallet, which is the imageHash
* of the wallet's configuration.
* @dev It is recommended to not have more than 200 signers as opcode repricing
* could make transactions impossible to execute as all the signers must be
* passed for each transaction.
*/
function deploy(address _mainModule, bytes32 _salt) external payable onlyRole(DEPLOYER_ROLE) returns (address _contract) {
bytes memory code = abi.encodePacked(Wallet.creationCode, uint256(uint160(_mainModule)));
assembly {
_contract := create2(callvalue(), add(code, 32), mload(code), _salt)
}
// check deployment success
require(_contract != address(0), 'WalletFactory: deployment failed');
// emit event, increases gas cost by ~2k
emit WalletDeployed(_contract, _mainModule, _salt);
}
}
11 changes: 11 additions & 0 deletions contracts/passport/IWalletProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright Immutable Pty Ltd 2018 - 2023
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.3;

/**
* Interface that WalletProxy.yul implements.
*/
interface IWalletProxy {
/// @dev Retrieve current implementation contract used by proxy
function PROXY_getImplementation() external view returns (address implementation);
}
72 changes: 72 additions & 0 deletions contracts/passport/MultiCallDeploy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) Immutable Pty Ltd 2018 - 2023
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.17;

import "./modules/commons/interfaces/IModuleCalls.sol";
import '@openzeppelin/contracts/access/AccessControl.sol';
import "./interfaces/IFactory.sol";

/**
* @title MultiCallDeploy
* @notice This contract is bundles the wallet deployment and the users first write transaction into a single transaction.
* Contract usage is intended for the submitter inside the relayer service, which will call either of the functions.
*/
contract MultiCallDeploy is AccessControl {
// Role to execute functions
bytes32 public constant EXECUTOR_ROLE = keccak256('EXECUTOR_ROLE');

constructor(address _admin, address _executor) {
_grantRole(DEFAULT_ADMIN_ROLE, _admin);
_grantRole(EXECUTOR_ROLE, _executor);
}

/*
* @dev Grants EXECUTOR_ROLE to an user.
* @param _executor Address that will be allowed to execute functions
*/
function grantExecutorRole(address _executor) external onlyRole(DEFAULT_ADMIN_ROLE) {
_grantRole(EXECUTOR_ROLE, _executor);
}

/*
* @dev Deploy wallet and execute transaction.
* @param _mainModule Address of the main module to be used by the wallet
* @param _salt Salt used to generate the address
* @param factory address of the factory contract
* @param _txs transaction to execute
* @param _nonce nonce of the wallet
* @param _signature transaction signature from wallet
*/
function deployExecute(address _mainModule, bytes32 _salt, address factory, IModuleCalls.Transaction[] calldata _txs, uint256 _nonce, bytes calldata _signature) external onlyRole(EXECUTOR_ROLE) {
address ret = IFactory(factory).deploy(_mainModule, _salt);
IModuleCalls(ret).execute(_txs, _nonce, _signature);
}

/*
* @dev Handles deployment of wallet and transaction execution for both cases
* @param cfa counter factual address of the wallet
* @param _mainModule Address of the main module to be used by the wallet
* @param _salt Salt used to generate the address
* @param factory address of the factory contract
* @param _txs transaction to execute
* @param _nonce nonce of the wallet
* @param _signature transaction signature from wallet
*/
function deployAndExecute(address cfa, address _mainModule, bytes32 _salt, address factory, IModuleCalls.Transaction[] calldata _txs, uint256 _nonce, bytes calldata _signature) external onlyRole(EXECUTOR_ROLE){
// Get code size at CFA
uint32 size;
assembly {
size := extcodesize(cfa)
}

// If size is 0, deploy the proxy and execute write tx
// Else, execute the users transaction
if (size == 0) {
address ret = IFactory(factory).deploy(_mainModule, _salt);
require(cfa == ret, "MultiCallDeploy: deployed address does not match CFA");
IModuleCalls(ret).execute(_txs, _nonce, _signature);
} else {
IModuleCalls(cfa).execute(_txs, _nonce, _signature);
}
}
}
1 change: 1 addition & 0 deletions contracts/passport/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DO NOT MODIFY THESE CONTRACTS DIRECTLY. This folder has been automatically extracted from https://github.com/immutable/wallet-contracts via a submodule in this repository's forks directory. See the upstream repository for full context.
10 changes: 10 additions & 0 deletions contracts/passport/Wallet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright Immutable Pty Ltd 2018 - 2023
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.17;

// Holds the creation code of the WalletProxy.yul used by smart contract wallet instances.
// Generate this bytecode using ./compileWalletProxyYul.sh
library Wallet {
// This bytecode must precisely match that in tests/utils/helpers.ts
bytes internal constant creationCode = hex"6054600f3d396034805130553df3fe63906111273d3560e01c14602b57363d3d373d3d3d3d369030545af43d82803e156027573d90f35b3d90fd5b30543d5260203df3";
}
81 changes: 81 additions & 0 deletions contracts/passport/WalletProxy.yul
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Immutable Pty Ltd 2018 - 2023
// SPDX-License-Identifier: Apache-2.0
//
// This Yul code creates a minimalist transparent proxy with a function to fetch
// the address of the contract being proxied to using the interface described in
// IWalletProxy.sol .
//
object "ProxyGetImplYul" {
// This is the initcode of the contract.
code {
// Copy the runtime code plus the address of the implementation
// parameter (32 bytes) which is appended to the end to memory.
// copy s bytes from code at position f to mem at position t
// codecopy(t, f, s)
// This will turn into a memory->memory copy for Ewasm and
// a codecopy for EVM
// The constant 0x54 is datasize("runtime") + 32. The solc compiler is
// unable to do constant addition as part of the compilation process, hence
// the constant.
// If the runtime code is to be updated, uncomment the following line, and comment
// out the following line, so that datasize("runtime") can be determined. It will
// be the byte following the 0x60 push1 opcode.
// datacopy(returndatasize(), dataoffset("runtime"), add(datasize("runtime"), 32))
datacopy(returndatasize(), dataoffset("runtime"), 0x54)

// Store the implementation address at the storage slot which is
// equivalent to the deployed address of this contract.
let implAddress := mload(datasize("runtime"))
sstore(address(), implAddress)

// now return the runtime object (the currently
// executing code is the constructor code)
return(returndatasize(), datasize("runtime"))
}


// Code for deployed contract
object "runtime" {
code {
// Load the function selector (the first four bytes of calldata) by shifting the
// word to the right.
let selector := shr(224, calldataload(returndatasize()))

if eq(selector, 0x90611127) /* Function selector for "PROXY_getImplementation()" */ {
let impl := sload(address())
mstore(returndatasize(), impl)
return(returndatasize(), 0x20)
}

// Load calldata to memory location 0.
// Copy s bytes from calldata at position f to mem at position t
// calldatacopy(t, f, s)
calldatacopy(returndatasize(), returndatasize(), calldatasize())

// Use returndatasize to load zero.
let zero := returndatasize()

// Execute delegate call. Have outsize set to zero, to indicate
// don't return any data automatically.
// Call contract at address a with input mem[in…(in+insize))
// providing g gas and v wei and output area
// mem[out…(out+outsize)) returning 0 on error
// (eg. out of gas) and 1 on success
// delegatecall(g, a, in, insize, out, outsize)
// Use sload(address()) to load the implemntation address.
let success := delegatecall(gas(), sload(address()), returndatasize(), calldatasize(), returndatasize(), returndatasize())

// Copy the return result to memory location 0.
// Copy s bytes from returndata at position f to mem at position t
// returndatacopy(t, f, s)
returndatacopy(zero, zero, returndatasize())

// Return or revert: memory location 0 contains either the return value
// or the revert information.
if iszero(success) {
revert (zero,returndatasize())
}
return (zero,returndatasize())
}
}
}
38 changes: 38 additions & 0 deletions contracts/passport/interfaces/IERC1271Wallet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.17;


interface IERC1271Wallet {

/**
* @notice Verifies whether the provided signature is valid with respect to the provided data
* @dev MUST return the correct magic value if the signature provided is valid for the provided data
* > The bytes4 magic value to return when signature is valid is 0x20c13b0b : bytes4(keccak256("isValidSignature(bytes,bytes)")
* > This function MAY modify Ethereum's state
* @param _data Arbitrary length data signed on the behalf of address(this)
* @param _signature Signature byte array associated with _data
* @return magicValue Magic value 0x20c13b0b if the signature is valid and 0x0 otherwise
*/
function isValidSignature(
bytes calldata _data,
bytes calldata _signature)
external
view
returns (bytes4 magicValue);

/**
* @notice Verifies whether the provided signature is valid with respect to the provided hash
* @dev MUST return the correct magic value if the signature provided is valid for the provided hash
* > The bytes4 magic value to return when signature is valid is 0x20c13b0b : bytes4(keccak256("isValidSignature(bytes,bytes)")
* > This function MAY modify Ethereum's state
* @param _hash keccak256 hash that was signed
* @param _signature Signature byte array associated with _data
* @return magicValue Magic value 0x20c13b0b if the signature is valid and 0x0 otherwise
*/
function isValidSignature(
bytes32 _hash,
bytes calldata _signature)
external
view
returns (bytes4 magicValue);
}
29 changes: 29 additions & 0 deletions contracts/passport/interfaces/IFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.17;

/**
* @title IFactory
* @notice Factory interface to interact with wallet factory
*/
interface IFactory {
event WalletDeployed(address indexed wallet, address indexed mainModule, bytes32 salt);

/**
* @notice Returns a deterministic contract address given a salt
* @param _mainModule Address of the main module to be used by the wallet
* @param _salt Salt used to generate the address
* @return _address The deterministic address
*/
function getAddress(address _mainModule, bytes32 _salt) external view returns (address);

/**
* @notice Will deploy a new wallet instance using create2
* @param _mainModule Address of the main module to be used by the wallet
* @param _salt Salt used to generate the wallet, which is the imageHash
* of the wallet's configuration.
* @dev It is recommended to not have more than 200 signers as opcode repricing
* could make transactions impossible to execute as all the signers must be
* passed for each transaction.
*/
function deploy(address _mainModule, bytes32 _salt) external payable returns (address);
}
8 changes: 8 additions & 0 deletions contracts/passport/interfaces/receivers/IERC1155Receiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.17;


interface IERC1155Receiver {
function onERC1155Received(address, address, uint256, uint256, bytes calldata) external returns (bytes4);
function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata) external returns (bytes4);
}
7 changes: 7 additions & 0 deletions contracts/passport/interfaces/receivers/IERC223Receiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.17;


interface IERC223Receiver {
function tokenFallback(address, uint256, bytes calldata) external;
}
7 changes: 7 additions & 0 deletions contracts/passport/interfaces/receivers/IERC721Receiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.17;


interface IERC721Receiver {
function onERC721Received(address, address, uint256, bytes calldata) external returns (bytes4);
}
26 changes: 26 additions & 0 deletions contracts/passport/migrations/Migrations.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.17;


contract Migrations {
address public owner;
uint public last_completed_migration;

constructor() public {
owner = msg.sender;
}

modifier restricted() {
if (msg.sender == owner)
_;
}

function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}

function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}
Loading
Loading