Transaction Hash:
Block:
21502899 at Dec-28-2024 06:57:47 PM +UTC
Transaction Fee:
0.000130835517550051 ETH
$0.29
Gas Used:
27,527 Gas / 4.752988613 Gwei
Emitted Events:
406 |
MultiSigWalletProxy.0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c( 0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c, 0x0000000000000000000000004838b106fce9647bdf1e7877bf73ce8b0bad5f97, 00000000000000000000000000000000000000000000000000675396317a1ee9 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x4838B106...B0BAD5f97
Miner
| (Titan Builder) |
11.722518837711944413 Eth
Nonce: 1054197
|
11.693304175052910097 Eth
Nonce: 1054198
| 0.029214662659034316 | |
0x74f306D6...5A8400302 | (Fee Recipient: 0x74f3...302) | 368.985315174583968923 Eth | 369.014399001725453188 Eth | 0.029083827141484265 |
Execution Trace
ETH 0.029083827141484265
MultiSigWalletProxy.CALL( )
- ETH 0.029083827141484265
MultiSigWalletImplementation.DELEGATECALL( )
File 1 of 2: MultiSigWalletProxy
File 2 of 2: MultiSigWalletImplementation
{"MultiSigWalletFactory.sol":{"content":"// SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.0;\n\nimport \"./MultiSigWalletProxy.sol\";\nimport \"./MultiSigWalletImplementation.sol\";\n\ncontract MultiSigWalletFactory {\n\n event NewMultiSigWalletCreated(address wallet);\n\n function createMultiSigWallet(\n address _implementation,\n address[] memory owners,\n uint required,\n uint256 nonce\n ) public payable returns (address payable) {\n bytes32 salt = keccak256(abi.encodePacked(nonce, owners, required));\n bytes memory initCode = abi.encodePacked(\n type(MultiSigWalletProxy).creationCode,\n abi.encode(address(_implementation), abi.encodeWithSignature(\"initialize(address[],uint256)\", owners, required))\n );\n\n address payable wallet;\n assembly {\n wallet := create2(0, add(initCode, 0x20), mload(initCode), salt)\n if iszero(extcodesize(wallet)) {\n revert(0, 0)\n }\n }\n\n emit NewMultiSigWalletCreated(wallet);\n\n return wallet;\n }\n \n function calculateMultiSigWalletAddress(\n address _implementation,\n address[] memory owners,\n uint required,\n uint256 nonce\n ) public view returns (address wallet) {\n bytes32 salt = keccak256(abi.encodePacked(nonce, owners, required));\n bytes memory initCode = abi.encodePacked(\n type(MultiSigWalletProxy).creationCode,\n abi.encode(address(_implementation), abi.encodeWithSignature(\"initialize(address[],uint256)\", owners, required))\n );\n bytes32 hash = keccak256(abi.encodePacked(\n bytes1(0xff),\n address(this),\n salt,\n keccak256(initCode)\n ));\n\n return address(uint160(uint(hash)));\n }\n\n function createMultiSigWalletWithTransaction(\n address _implementation,\n address[] memory owners,\n uint required,\n uint256 nonce,\n MultiSigWalletImplementation.Transaction memory transaction,\n MultiSigWalletImplementation.Signature[] memory signatures\n ) public payable returns (address payable, bool) {\n address payable wallet = createMultiSigWallet(_implementation, owners, required, nonce);\n bool isOk = MultiSigWalletImplementation(wallet).batchSignature(transaction, signatures);\n return (wallet, isOk);\n }\n}"},"MultiSigWalletImplementation.sol":{"content":"// SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.0;\n\n/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.\n/// @author Stefan George - \[email protected]\u003e\ncontract MultiSigWalletImplementation {\n /*\n * Events\n */\n event Confirmation(address indexed sender, uint indexed transactionId);\n event Revocation(address indexed sender, uint indexed transactionId);\n event Submission(uint indexed transactionId);\n event Execution(uint indexed transactionId);\n event ExecutionFailure(uint indexed transactionId);\n event Deposit(address indexed sender, uint value);\n event OwnerAddition(address indexed owner);\n event OwnerRemoval(address indexed owner);\n event RequirementChange(uint required);\n\n /*\n * Constants\n */\n uint public constant MAX_OWNER_COUNT = 50;\n\n /*\n * Storage\n */\n mapping(uint =\u003e Transaction) public transactions;\n mapping(uint =\u003e mapping(address =\u003e bool)) public confirmations;\n mapping(address =\u003e bool) public isOwner;\n mapping(bytes32 =\u003e bool) public txNonces;\n\n address[] public owners;\n uint public required;\n uint public transactionCount;\n\n bytes32 public DOMAIN_SEPARATOR;\n bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\");\n bytes32 internal constant TRANSACTION_TYPEHASH = keccak256(\"Transaction(uint nonce,address destination,uint value,bytes data)\");\n\n bool initialized;\n\n struct Transaction {\n uint nonce;\n address destination;\n uint value;\n bytes data;\n bool executed;\n }\n\n struct Signature {\n address signer;\n uint8 v;\n bytes32 r;\n bytes32 s;\n }\n\n /*\n * Modifiers\n */\n modifier onlyWallet() {\n require(msg.sender == address(this));\n _;\n }\n\n modifier ownerDoesNotExist(address owner) {\n require(!isOwner[owner]);\n _;\n }\n\n modifier ownerExists(address owner) {\n require(isOwner[owner]);\n _;\n }\n\n modifier transactionExists(uint transactionId) {\n require(transactions[transactionId].destination != address(0));\n _;\n }\n\n modifier confirmed(uint transactionId, address owner) {\n require(confirmations[transactionId][owner]);\n _;\n }\n\n modifier notConfirmed(uint transactionId, address owner) {\n require(!confirmations[transactionId][owner]);\n _;\n }\n\n modifier notExecuted(uint transactionId) {\n require(!transactions[transactionId].executed);\n _;\n }\n\n modifier notNull(address _address) {\n require(_address != address(0));\n _;\n }\n\n modifier validRequirement(uint ownerCount, uint _required) {\n require(\n ownerCount \u003c= MAX_OWNER_COUNT \u0026\u0026\n _required \u003c= ownerCount \u0026\u0026\n _required != 0 \u0026\u0026\n ownerCount != 0\n );\n _;\n }\n\n /// @dev Receive function allows to deposit ether.\n receive() external payable {\n if (msg.value \u003e 0) emit Deposit(msg.sender, msg.value);\n }\n\n /// @dev Fallback function allows to deposit ether.\n fallback() external payable {\n if (msg.value \u003e 0) emit Deposit(msg.sender, msg.value);\n }\n\n /*\n * Public functions\n */\n /// @dev Contract constructor sets initial owners and required number of confirmations.\n constructor() {}\n\n function initialize(\n address[] memory _owners,\n uint _required\n ) validRequirement(_owners.length, _required) public {\n require(!initialized, \"already initialized\");\n\n DOMAIN_SEPARATOR = keccak256(\n abi.encode(\n EIP712DOMAIN_TYPEHASH,\n keccak256(\"MultiSigWallet\"), // name\n keccak256(\"2\"), // version\n block.chainid,\n address(this)\n )\n );\n\n for (uint i = 0; i \u003c _owners.length; i++) {\n require(!isOwner[_owners[i]] \u0026\u0026 _owners[i] != address(0));\n isOwner[_owners[i]] = true;\n }\n owners = _owners;\n required = _required;\n\n initialized = true;\n }\n \n\n /// @dev Allows to add a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of new owner.\n function addOwner(\n address owner\n )\n public\n onlyWallet\n ownerDoesNotExist(owner)\n notNull(owner)\n validRequirement(owners.length + 1, required)\n {\n isOwner[owner] = true;\n owners.push(owner);\n emit OwnerAddition(owner);\n }\n\n /// @dev Allows to remove an owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner.\n function removeOwner(address owner) public onlyWallet ownerExists(owner) {\n isOwner[owner] = false;\n for (uint i = 0; i \u003c owners.length - 1; i++)\n if (owners[i] == owner) {\n owners[i] = owners[owners.length - 1];\n delete owners[i];\n break;\n }\n if (required \u003e owners.length) changeRequirement(owners.length);\n emit OwnerRemoval(owner);\n }\n\n /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner to be replaced.\n /// @param newOwner Address of new owner.\n function replaceOwner(\n address owner,\n address newOwner\n ) public onlyWallet ownerExists(owner) ownerDoesNotExist(newOwner) {\n for (uint i = 0; i \u003c owners.length; i++)\n if (owners[i] == owner) {\n owners[i] = newOwner;\n break;\n }\n isOwner[owner] = false;\n isOwner[newOwner] = true;\n emit OwnerRemoval(owner);\n emit OwnerAddition(newOwner);\n }\n\n /// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet.\n /// @param _required Number of required confirmations.\n function changeRequirement(\n uint _required\n ) public onlyWallet validRequirement(owners.length, _required) {\n required = _required;\n emit RequirementChange(_required);\n }\n\n /// @dev Allows an owner to submit and confirm a transaction.\n /// @param destination Transaction target address.\n /// @param value Transaction ether value.\n /// @param data Transaction data payload.\n /// @return transactionId Returns transaction ID.\n function submitTransaction(\n address destination,\n uint value,\n bytes memory data\n ) public returns (uint transactionId) {\n transactionId = addTransaction(destination, value, data);\n confirmTransaction(transactionId);\n }\n\n /// @dev Allows an owner to confirm a transaction.\n /// @param transactionId Transaction ID.\n function confirmTransaction(\n uint transactionId\n )\n public\n ownerExists(msg.sender)\n transactionExists(transactionId)\n notConfirmed(transactionId, msg.sender)\n {\n confirmations[transactionId][msg.sender] = true;\n emit Confirmation(msg.sender, transactionId);\n executeTransaction(transactionId);\n }\n\n /// @dev Allows an owner to revoke a confirmation for a transaction.\n /// @param transactionId Transaction ID.\n function revokeConfirmation(\n uint transactionId\n )\n public\n ownerExists(msg.sender)\n confirmed(transactionId, msg.sender)\n notExecuted(transactionId)\n {\n confirmations[transactionId][msg.sender] = false;\n emit Revocation(msg.sender, transactionId);\n }\n\n /// @dev Allows anyone to execute a confirmed transaction.\n /// @param transactionId Transaction ID.\n function executeTransaction(\n uint transactionId\n )\n public\n ownerExists(msg.sender)\n confirmed(transactionId, msg.sender)\n notExecuted(transactionId)\n {\n _executeTransaction(transactionId);\n }\n\n function _executeTransaction(\n uint transactionId\n ) internal notExecuted(transactionId) {\n if (isConfirmed(transactionId)) {\n Transaction storage txn = transactions[transactionId];\n txn.executed = true;\n if (\n external_call(\n txn.destination,\n txn.value,\n txn.data.length,\n txn.data\n )\n ) emit Execution(transactionId);\n else {\n emit ExecutionFailure(transactionId);\n txn.executed = false;\n }\n }\n }\n\n // call has been separated into its own function in order to take advantage\n // of the Solidity\u0027s code generator to produce a loop that copies tx.data into memory.\n function external_call(\n address destination,\n uint value,\n uint dataLength,\n bytes memory data\n ) internal returns (bool) {\n bool result;\n assembly {\n let x := mload(0x40) // \"Allocate\" memory for output (0x40 is where \"free memory\" pointer is stored by convention)\n let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that\n result := call(\n sub(gas(), 34710), // 34710 is the value that solidity is currently emitting\n // It includes callGas (700) + callVeryLow (3, to pay for SUB) + callValueTransferGas (9000) +\n // callNewAccountGas (25000, in case the destination address does not exist and needs creating)\n destination,\n value,\n d,\n dataLength, // Size of the input (in bytes) - this is what fixes the padding problem\n x,\n 0 // Output is ignored, therefore the output size is zero\n )\n }\n return result;\n }\n\n /// @dev Returns the confirmation status of a transaction.\n /// @param transactionId Transaction ID.\n /// @return status Confirmation status.\n function isConfirmed(uint transactionId) public view returns (bool status) {\n uint count = 0;\n for (uint i = 0; i \u003c owners.length; i++) {\n if (confirmations[transactionId][owners[i]]) count += 1;\n if (count == required) return true;\n }\n }\n\n /*\n * Internal functions\n */\n /// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.\n /// @param destination Transaction target address.\n /// @param value Transaction ether value.\n /// @param data Transaction data payload.\n /// @return transactionId Returns transaction ID.\n function addTransaction(\n address destination,\n uint value,\n bytes memory data\n ) internal notNull(destination) returns (uint transactionId) {\n if(transactions[transactionId].destination == address(0)) {\n transactionId = transactionCount;\n transactions[transactionId] = Transaction({\n nonce: transactionId,\n destination: destination,\n value: value,\n data: data,\n executed: false\n });\n transactionCount += 1;\n \n emit Submission(transactionId);\n } else {\n revert(\"transactionId already exist\");\n }\n }\n\n /*\n * Web3 call functions\n */\n /// @dev Returns number of confirmations of a transaction.\n /// @param transactionId Transaction ID.\n /// @return count Number of confirmations.\n function getConfirmationCount(\n uint transactionId\n ) public view returns (uint count) {\n for (uint i = 0; i \u003c owners.length; i++)\n if (confirmations[transactionId][owners[i]]) count += 1;\n }\n\n /// @dev Returns total number of transactions after filers are applied.\n /// @param pending Include pending transactions.\n /// @param executed Include executed transactions.\n /// @return count Total number of transactions after filters are applied.\n function getTransactionCount(\n bool pending,\n bool executed\n ) public view returns (uint count) {\n for (uint i = 0; i \u003c transactionCount; i++)\n if (\n (pending \u0026\u0026 !transactions[i].executed) ||\n (executed \u0026\u0026 transactions[i].executed)\n ) count += 1;\n }\n\n /// @dev Returns list of owners.\n /// @return List of owner addresses.\n function getOwners() public view returns (address[] memory) {\n return owners;\n }\n\n /// @dev Returns array with owner addresses, which confirmed transaction.\n /// @param transactionId Transaction ID.\n /// @return _confirmations Returns array of owner addresses.\n function getConfirmations(\n uint transactionId\n ) public view returns (address[] memory _confirmations) {\n address[] memory confirmationsTemp = new address[](owners.length);\n uint count = 0;\n uint i;\n for (i = 0; i \u003c owners.length; i++)\n if (confirmations[transactionId][owners[i]]) {\n confirmationsTemp[count] = owners[i];\n count += 1;\n }\n _confirmations = new address[](count);\n for (i = 0; i \u003c count; i++) _confirmations[i] = confirmationsTemp[i];\n }\n\n /// @dev Returns list of transaction IDs in defined range.\n /// @param from Index start position of transaction array.\n /// @param to Index end position of transaction array.\n /// @param pending Include pending transactions.\n /// @param executed Include executed transactions.\n /// @return _transactionIds Returns array of transaction IDs.\n function getTransactionIds(\n uint from,\n uint to,\n bool pending,\n bool executed\n ) public view returns (uint[] memory _transactionIds) {\n uint[] memory transactionIdsTemp = new uint[](transactionCount);\n uint count = 0;\n uint i;\n for (i = 0; i \u003c transactionCount; i++)\n if (\n (pending \u0026\u0026 !transactions[i].executed) ||\n (executed \u0026\u0026 transactions[i].executed)\n ) {\n transactionIdsTemp[count] = i;\n count += 1;\n }\n _transactionIds = new uint[](to - from);\n for (i = from; i \u003c to; i++)\n _transactionIds[i - from] = transactionIdsTemp[i];\n }\n\n function hashTransaction(\n Transaction memory transaction\n ) public pure returns (bytes32) {\n return\n keccak256(\n abi.encode(\n TRANSACTION_TYPEHASH,\n transaction.nonce,\n transaction.destination,\n transaction.value,\n keccak256(bytes(transaction.data))\n )\n );\n }\n\n function getTransactionMessage(\n Transaction memory transaction\n ) public view returns (bytes32) {\n bytes32 digest = keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR,\n hashTransaction(transaction)\n )\n );\n return digest;\n }\n\n function batchSignature(Transaction memory txn, Signature[] memory sortedSignatures) public returns (bool isOK) {\n require(sortedSignatures.length \u003e= required, \"invalid signature data length\");\n\n bytes32 digest = keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR,\n hashTransaction(txn)\n )\n );\n require(!txNonces[digest], \"tx-executed\");\n\n uint256 txId = txn.nonce;\n address lastOwner = address(0);\n for(uint i = 0; i \u003c sortedSignatures.length; i++) {\n Signature memory signature = sortedSignatures[i];\n address signer = signature.signer;\n uint8 v = signature.v;\n bytes32 r = signature.r;\n bytes32 s = signature.s;\n\n address currentOwner = ecrecover(digest, v, r, s);\n\n // to save gas, must need signature.signer sorted\n require(currentOwner \u003e lastOwner \u0026\u0026 isOwner[currentOwner] \u0026\u0026 signer == currentOwner, \"error-sig\");\n lastOwner = currentOwner;\n emit Confirmation(signer, txId);\n }\n\n txn.executed = true;\n txNonces[digest] = true;\n if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) {\n emit Execution(txId);\n } else {\n emit ExecutionFailure(txId);\n txn.executed = false;\n }\n\n return txn.executed;\n }\n\n struct Call {\n address target;\n uint value;\n bytes data;\n }\n\n function multiCall(\n Call[] memory calls\n ) public onlyWallet {\n for (uint i = 0; i \u003c calls.length; i++) {\n if (external_call(\n calls[i].target,\n calls[i].value,\n calls[i].data.length,\n calls[i].data\n )) {}\n else {\n revert(\"internal call failed\");\n }\n }\n }\n}\n"},"MultiSigWalletImplementationBeacon.sol":{"content":"// SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.0;\n\nimport \"./MultiSigWalletImplementation.sol\";\n\n\ncontract MultiSigWalletImplementationBeacon {\n\n event MultiSigWalletImplementationDeployed(address indexed implementation);\n\n constructor() {\n MultiSigWalletImplementation implementation = new MultiSigWalletImplementation();\n\n address[] memory owners = new address[](1);\n owners[0] = msg.sender;\n\n implementation.initialize(owners, 1);\n \n emit MultiSigWalletImplementationDeployed(address(implementation));\n }\n}"},"MultiSigWalletProxy.sol":{"content":"// SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.0;\n\ncontract MultiSigWalletProxy {\n address public implementation;\n\n constructor(address _implementation, bytes memory _data) {\n implementation = _implementation;\n if(_data.length \u003e 0) {\n (bool success,) = _implementation.delegatecall(_data);\n require(success, \"MultiSigWalletProxy: Initialization failed\");\n }\n }\n\n fallback() external payable {\n _delegate(implementation);\n }\n\n receive() external payable {\n _delegate(implementation);\n }\n\n function _delegate(address _implementation) internal {\n assembly {\n calldatacopy(0, 0, calldatasize())\n let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0)\n returndatacopy(0, 0, returndatasize())\n switch result\n case 0 { \n revert(0, returndatasize()) \n } default { \n return(0, returndatasize())\n }\n }\n }\n}"}}
File 2 of 2: MultiSigWalletImplementation
{"MultiSigWalletFactory.sol":{"content":"// SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.0;\n\nimport \"./MultiSigWalletProxy.sol\";\nimport \"./MultiSigWalletImplementation.sol\";\n\ncontract MultiSigWalletFactory {\n\n event NewMultiSigWalletCreated(address wallet);\n\n function createMultiSigWallet(\n address _implementation,\n address[] memory owners,\n uint required,\n uint256 nonce\n ) public payable returns (address payable) {\n bytes32 salt = keccak256(abi.encodePacked(nonce, owners, required));\n bytes memory initCode = abi.encodePacked(\n type(MultiSigWalletProxy).creationCode,\n abi.encode(address(_implementation), abi.encodeWithSignature(\"initialize(address[],uint256)\", owners, required))\n );\n\n address payable wallet;\n assembly {\n wallet := create2(0, add(initCode, 0x20), mload(initCode), salt)\n if iszero(extcodesize(wallet)) {\n revert(0, 0)\n }\n }\n\n emit NewMultiSigWalletCreated(wallet);\n\n return wallet;\n }\n \n function calculateMultiSigWalletAddress(\n address _implementation,\n address[] memory owners,\n uint required,\n uint256 nonce\n ) public view returns (address wallet) {\n bytes32 salt = keccak256(abi.encodePacked(nonce, owners, required));\n bytes memory initCode = abi.encodePacked(\n type(MultiSigWalletProxy).creationCode,\n abi.encode(address(_implementation), abi.encodeWithSignature(\"initialize(address[],uint256)\", owners, required))\n );\n bytes32 hash = keccak256(abi.encodePacked(\n bytes1(0xff),\n address(this),\n salt,\n keccak256(initCode)\n ));\n\n return address(uint160(uint(hash)));\n }\n\n function createMultiSigWalletWithTransaction(\n address _implementation,\n address[] memory owners,\n uint required,\n uint256 nonce,\n MultiSigWalletImplementation.Transaction memory transaction,\n MultiSigWalletImplementation.Signature[] memory signatures\n ) public payable returns (address payable, bool) {\n address payable wallet = createMultiSigWallet(_implementation, owners, required, nonce);\n bool isOk = MultiSigWalletImplementation(wallet).batchSignature(transaction, signatures);\n return (wallet, isOk);\n }\n}"},"MultiSigWalletImplementation.sol":{"content":"// SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.0;\n\n/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.\n/// @author Stefan George - \[email protected]\u003e\ncontract MultiSigWalletImplementation {\n /*\n * Events\n */\n event Confirmation(address indexed sender, uint indexed transactionId);\n event Revocation(address indexed sender, uint indexed transactionId);\n event Submission(uint indexed transactionId);\n event Execution(uint indexed transactionId);\n event ExecutionFailure(uint indexed transactionId);\n event Deposit(address indexed sender, uint value);\n event OwnerAddition(address indexed owner);\n event OwnerRemoval(address indexed owner);\n event RequirementChange(uint required);\n\n /*\n * Constants\n */\n uint public constant MAX_OWNER_COUNT = 50;\n\n /*\n * Storage\n */\n mapping(uint =\u003e Transaction) public transactions;\n mapping(uint =\u003e mapping(address =\u003e bool)) public confirmations;\n mapping(address =\u003e bool) public isOwner;\n mapping(bytes32 =\u003e bool) public txNonces;\n\n address[] public owners;\n uint public required;\n uint public transactionCount;\n\n bytes32 public DOMAIN_SEPARATOR;\n bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\");\n bytes32 internal constant TRANSACTION_TYPEHASH = keccak256(\"Transaction(uint nonce,address destination,uint value,bytes data)\");\n\n bool initialized;\n\n struct Transaction {\n uint nonce;\n address destination;\n uint value;\n bytes data;\n bool executed;\n }\n\n struct Signature {\n address signer;\n uint8 v;\n bytes32 r;\n bytes32 s;\n }\n\n /*\n * Modifiers\n */\n modifier onlyWallet() {\n require(msg.sender == address(this));\n _;\n }\n\n modifier ownerDoesNotExist(address owner) {\n require(!isOwner[owner]);\n _;\n }\n\n modifier ownerExists(address owner) {\n require(isOwner[owner]);\n _;\n }\n\n modifier transactionExists(uint transactionId) {\n require(transactions[transactionId].destination != address(0));\n _;\n }\n\n modifier confirmed(uint transactionId, address owner) {\n require(confirmations[transactionId][owner]);\n _;\n }\n\n modifier notConfirmed(uint transactionId, address owner) {\n require(!confirmations[transactionId][owner]);\n _;\n }\n\n modifier notExecuted(uint transactionId) {\n require(!transactions[transactionId].executed);\n _;\n }\n\n modifier notNull(address _address) {\n require(_address != address(0));\n _;\n }\n\n modifier validRequirement(uint ownerCount, uint _required) {\n require(\n ownerCount \u003c= MAX_OWNER_COUNT \u0026\u0026\n _required \u003c= ownerCount \u0026\u0026\n _required != 0 \u0026\u0026\n ownerCount != 0\n );\n _;\n }\n\n /// @dev Receive function allows to deposit ether.\n receive() external payable {\n if (msg.value \u003e 0) emit Deposit(msg.sender, msg.value);\n }\n\n /// @dev Fallback function allows to deposit ether.\n fallback() external payable {\n if (msg.value \u003e 0) emit Deposit(msg.sender, msg.value);\n }\n\n /*\n * Public functions\n */\n /// @dev Contract constructor sets initial owners and required number of confirmations.\n constructor() {}\n\n function initialize(\n address[] memory _owners,\n uint _required\n ) validRequirement(_owners.length, _required) public {\n require(!initialized, \"already initialized\");\n\n DOMAIN_SEPARATOR = keccak256(\n abi.encode(\n EIP712DOMAIN_TYPEHASH,\n keccak256(\"MultiSigWallet\"), // name\n keccak256(\"2\"), // version\n block.chainid,\n address(this)\n )\n );\n\n for (uint i = 0; i \u003c _owners.length; i++) {\n require(!isOwner[_owners[i]] \u0026\u0026 _owners[i] != address(0));\n isOwner[_owners[i]] = true;\n }\n owners = _owners;\n required = _required;\n\n initialized = true;\n }\n \n\n /// @dev Allows to add a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of new owner.\n function addOwner(\n address owner\n )\n public\n onlyWallet\n ownerDoesNotExist(owner)\n notNull(owner)\n validRequirement(owners.length + 1, required)\n {\n isOwner[owner] = true;\n owners.push(owner);\n emit OwnerAddition(owner);\n }\n\n /// @dev Allows to remove an owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner.\n function removeOwner(address owner) public onlyWallet ownerExists(owner) {\n isOwner[owner] = false;\n for (uint i = 0; i \u003c owners.length - 1; i++)\n if (owners[i] == owner) {\n owners[i] = owners[owners.length - 1];\n delete owners[i];\n break;\n }\n if (required \u003e owners.length) changeRequirement(owners.length);\n emit OwnerRemoval(owner);\n }\n\n /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner to be replaced.\n /// @param newOwner Address of new owner.\n function replaceOwner(\n address owner,\n address newOwner\n ) public onlyWallet ownerExists(owner) ownerDoesNotExist(newOwner) {\n for (uint i = 0; i \u003c owners.length; i++)\n if (owners[i] == owner) {\n owners[i] = newOwner;\n break;\n }\n isOwner[owner] = false;\n isOwner[newOwner] = true;\n emit OwnerRemoval(owner);\n emit OwnerAddition(newOwner);\n }\n\n /// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet.\n /// @param _required Number of required confirmations.\n function changeRequirement(\n uint _required\n ) public onlyWallet validRequirement(owners.length, _required) {\n required = _required;\n emit RequirementChange(_required);\n }\n\n /// @dev Allows an owner to submit and confirm a transaction.\n /// @param destination Transaction target address.\n /// @param value Transaction ether value.\n /// @param data Transaction data payload.\n /// @return transactionId Returns transaction ID.\n function submitTransaction(\n address destination,\n uint value,\n bytes memory data\n ) public returns (uint transactionId) {\n transactionId = addTransaction(destination, value, data);\n confirmTransaction(transactionId);\n }\n\n /// @dev Allows an owner to confirm a transaction.\n /// @param transactionId Transaction ID.\n function confirmTransaction(\n uint transactionId\n )\n public\n ownerExists(msg.sender)\n transactionExists(transactionId)\n notConfirmed(transactionId, msg.sender)\n {\n confirmations[transactionId][msg.sender] = true;\n emit Confirmation(msg.sender, transactionId);\n executeTransaction(transactionId);\n }\n\n /// @dev Allows an owner to revoke a confirmation for a transaction.\n /// @param transactionId Transaction ID.\n function revokeConfirmation(\n uint transactionId\n )\n public\n ownerExists(msg.sender)\n confirmed(transactionId, msg.sender)\n notExecuted(transactionId)\n {\n confirmations[transactionId][msg.sender] = false;\n emit Revocation(msg.sender, transactionId);\n }\n\n /// @dev Allows anyone to execute a confirmed transaction.\n /// @param transactionId Transaction ID.\n function executeTransaction(\n uint transactionId\n )\n public\n ownerExists(msg.sender)\n confirmed(transactionId, msg.sender)\n notExecuted(transactionId)\n {\n _executeTransaction(transactionId);\n }\n\n function _executeTransaction(\n uint transactionId\n ) internal notExecuted(transactionId) {\n if (isConfirmed(transactionId)) {\n Transaction storage txn = transactions[transactionId];\n txn.executed = true;\n if (\n external_call(\n txn.destination,\n txn.value,\n txn.data.length,\n txn.data\n )\n ) emit Execution(transactionId);\n else {\n emit ExecutionFailure(transactionId);\n txn.executed = false;\n }\n }\n }\n\n // call has been separated into its own function in order to take advantage\n // of the Solidity\u0027s code generator to produce a loop that copies tx.data into memory.\n function external_call(\n address destination,\n uint value,\n uint dataLength,\n bytes memory data\n ) internal returns (bool) {\n bool result;\n assembly {\n let x := mload(0x40) // \"Allocate\" memory for output (0x40 is where \"free memory\" pointer is stored by convention)\n let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that\n result := call(\n sub(gas(), 34710), // 34710 is the value that solidity is currently emitting\n // It includes callGas (700) + callVeryLow (3, to pay for SUB) + callValueTransferGas (9000) +\n // callNewAccountGas (25000, in case the destination address does not exist and needs creating)\n destination,\n value,\n d,\n dataLength, // Size of the input (in bytes) - this is what fixes the padding problem\n x,\n 0 // Output is ignored, therefore the output size is zero\n )\n }\n return result;\n }\n\n /// @dev Returns the confirmation status of a transaction.\n /// @param transactionId Transaction ID.\n /// @return status Confirmation status.\n function isConfirmed(uint transactionId) public view returns (bool status) {\n uint count = 0;\n for (uint i = 0; i \u003c owners.length; i++) {\n if (confirmations[transactionId][owners[i]]) count += 1;\n if (count == required) return true;\n }\n }\n\n /*\n * Internal functions\n */\n /// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.\n /// @param destination Transaction target address.\n /// @param value Transaction ether value.\n /// @param data Transaction data payload.\n /// @return transactionId Returns transaction ID.\n function addTransaction(\n address destination,\n uint value,\n bytes memory data\n ) internal notNull(destination) returns (uint transactionId) {\n if(transactions[transactionId].destination == address(0)) {\n transactionId = transactionCount;\n transactions[transactionId] = Transaction({\n nonce: transactionId,\n destination: destination,\n value: value,\n data: data,\n executed: false\n });\n transactionCount += 1;\n \n emit Submission(transactionId);\n } else {\n revert(\"transactionId already exist\");\n }\n }\n\n /*\n * Web3 call functions\n */\n /// @dev Returns number of confirmations of a transaction.\n /// @param transactionId Transaction ID.\n /// @return count Number of confirmations.\n function getConfirmationCount(\n uint transactionId\n ) public view returns (uint count) {\n for (uint i = 0; i \u003c owners.length; i++)\n if (confirmations[transactionId][owners[i]]) count += 1;\n }\n\n /// @dev Returns total number of transactions after filers are applied.\n /// @param pending Include pending transactions.\n /// @param executed Include executed transactions.\n /// @return count Total number of transactions after filters are applied.\n function getTransactionCount(\n bool pending,\n bool executed\n ) public view returns (uint count) {\n for (uint i = 0; i \u003c transactionCount; i++)\n if (\n (pending \u0026\u0026 !transactions[i].executed) ||\n (executed \u0026\u0026 transactions[i].executed)\n ) count += 1;\n }\n\n /// @dev Returns list of owners.\n /// @return List of owner addresses.\n function getOwners() public view returns (address[] memory) {\n return owners;\n }\n\n /// @dev Returns array with owner addresses, which confirmed transaction.\n /// @param transactionId Transaction ID.\n /// @return _confirmations Returns array of owner addresses.\n function getConfirmations(\n uint transactionId\n ) public view returns (address[] memory _confirmations) {\n address[] memory confirmationsTemp = new address[](owners.length);\n uint count = 0;\n uint i;\n for (i = 0; i \u003c owners.length; i++)\n if (confirmations[transactionId][owners[i]]) {\n confirmationsTemp[count] = owners[i];\n count += 1;\n }\n _confirmations = new address[](count);\n for (i = 0; i \u003c count; i++) _confirmations[i] = confirmationsTemp[i];\n }\n\n /// @dev Returns list of transaction IDs in defined range.\n /// @param from Index start position of transaction array.\n /// @param to Index end position of transaction array.\n /// @param pending Include pending transactions.\n /// @param executed Include executed transactions.\n /// @return _transactionIds Returns array of transaction IDs.\n function getTransactionIds(\n uint from,\n uint to,\n bool pending,\n bool executed\n ) public view returns (uint[] memory _transactionIds) {\n uint[] memory transactionIdsTemp = new uint[](transactionCount);\n uint count = 0;\n uint i;\n for (i = 0; i \u003c transactionCount; i++)\n if (\n (pending \u0026\u0026 !transactions[i].executed) ||\n (executed \u0026\u0026 transactions[i].executed)\n ) {\n transactionIdsTemp[count] = i;\n count += 1;\n }\n _transactionIds = new uint[](to - from);\n for (i = from; i \u003c to; i++)\n _transactionIds[i - from] = transactionIdsTemp[i];\n }\n\n function hashTransaction(\n Transaction memory transaction\n ) public pure returns (bytes32) {\n return\n keccak256(\n abi.encode(\n TRANSACTION_TYPEHASH,\n transaction.nonce,\n transaction.destination,\n transaction.value,\n keccak256(bytes(transaction.data))\n )\n );\n }\n\n function getTransactionMessage(\n Transaction memory transaction\n ) public view returns (bytes32) {\n bytes32 digest = keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR,\n hashTransaction(transaction)\n )\n );\n return digest;\n }\n\n function batchSignature(Transaction memory txn, Signature[] memory sortedSignatures) public returns (bool isOK) {\n require(sortedSignatures.length \u003e= required, \"invalid signature data length\");\n\n bytes32 digest = keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR,\n hashTransaction(txn)\n )\n );\n require(!txNonces[digest], \"tx-executed\");\n\n uint256 txId = txn.nonce;\n address lastOwner = address(0);\n for(uint i = 0; i \u003c sortedSignatures.length; i++) {\n Signature memory signature = sortedSignatures[i];\n address signer = signature.signer;\n uint8 v = signature.v;\n bytes32 r = signature.r;\n bytes32 s = signature.s;\n\n address currentOwner = ecrecover(digest, v, r, s);\n\n // to save gas, must need signature.signer sorted\n require(currentOwner \u003e lastOwner \u0026\u0026 isOwner[currentOwner] \u0026\u0026 signer == currentOwner, \"error-sig\");\n lastOwner = currentOwner;\n emit Confirmation(signer, txId);\n }\n\n txn.executed = true;\n txNonces[digest] = true;\n if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) {\n emit Execution(txId);\n } else {\n emit ExecutionFailure(txId);\n txn.executed = false;\n }\n\n return txn.executed;\n }\n\n struct Call {\n address target;\n uint value;\n bytes data;\n }\n\n function multiCall(\n Call[] memory calls\n ) public onlyWallet {\n for (uint i = 0; i \u003c calls.length; i++) {\n if (external_call(\n calls[i].target,\n calls[i].value,\n calls[i].data.length,\n calls[i].data\n )) {}\n else {\n revert(\"internal call failed\");\n }\n }\n }\n}\n"},"MultiSigWalletImplementationBeacon.sol":{"content":"// SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.0;\n\nimport \"./MultiSigWalletImplementation.sol\";\n\n\ncontract MultiSigWalletImplementationBeacon {\n\n event MultiSigWalletImplementationDeployed(address indexed implementation);\n\n constructor() {\n MultiSigWalletImplementation implementation = new MultiSigWalletImplementation();\n\n address[] memory owners = new address[](1);\n owners[0] = msg.sender;\n\n implementation.initialize(owners, 1);\n \n emit MultiSigWalletImplementationDeployed(address(implementation));\n }\n}"},"MultiSigWalletProxy.sol":{"content":"// SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.0;\n\ncontract MultiSigWalletProxy {\n address public implementation;\n\n constructor(address _implementation, bytes memory _data) {\n implementation = _implementation;\n if(_data.length \u003e 0) {\n (bool success,) = _implementation.delegatecall(_data);\n require(success, \"MultiSigWalletProxy: Initialization failed\");\n }\n }\n\n fallback() external payable {\n _delegate(implementation);\n }\n\n receive() external payable {\n _delegate(implementation);\n }\n\n function _delegate(address _implementation) internal {\n assembly {\n calldatacopy(0, 0, calldatasize())\n let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0)\n returndatacopy(0, 0, returndatasize())\n switch result\n case 0 { \n revert(0, returndatasize()) \n } default { \n return(0, returndatasize())\n }\n }\n }\n}"}}