ETH Price: $2,235.82 (-7.12%)

Transaction Decoder

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 Code
(Titan Builder)
11.722518837711944413 Eth
Nonce: 1054197
11.693304175052910097 Eth
Nonce: 1054198
0.029214662659034316
0x74f306D6...5A8400302
(Fee Recipient: 0x74f3...302)
368.985315174583968923 Eth369.014399001725453188 Eth0.029083827141484265

Execution Trace

ETH 0.029083827141484265 MultiSigWalletProxy.CALL( )
  • ETH 0.029083827141484265 MultiSigWalletImplementation.DELEGATECALL( )
    File 1 of 2: MultiSigWalletProxy
    {"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}"}}