ETH Price: $2,541.62 (+0.50%)
Gas: 4.15 Gwei

Transaction Decoder

Block:
18778269 at Dec-13-2023 03:54:35 PM +UTC
Transaction Fee:
0.009078025924572 ETH $23.07
Gas Used:
132,000 Gas / 68.772923671 Gwei

Emitted Events:

182 DepositContract.DepositEvent( pubkey=0xB1473277B9C6BEBCE0C706BD198356A09C73FEBBE7925D85AC43836E412D10629D5E14883F54C066DE204C35D5897E48, withdrawal_credentials=0x010000000000000000000000C0B16B40C6079B0A317A2FEBC062509CDF447F5C, amount=0x0040597307000000, signature=0xB72403C96F249F0BFF62E9ACFA63D4D35B1647EDD74A4DEF40C06CC450D4E009115052DDC4C710F3449B82322768C443130BAAB9650BEAC29020E616C6B09E89655ADC5595C326EF6CC170160EA778D583B22C4E478CCF528A2167DD27FCAAAF, index=0xF0FA100000000000 )
183 FigmentEth2Depositor.DepositEvent( from=[Receiver] WalletSimple, nodesAmount=1 )
184 WalletSimple.Transacted( msgSender=[Sender] 0x91d805fe733d80d86f7e6dfa22259decc9b16628, otherSigner=0x948Bdbe1...6167B5cFF, operation=A051B4D8100D003005C5F5AB4E1E23CD13D7890B87BE15DD42A6D25D08EFDF33, toAddress=FigmentEth2Depositor, value=32000000000000000000, data=0x4F498C730000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001A00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030B1473277B9C6BEBCE0C706BD198356A09C73FEBBE7925D85AC43836E412D10629D5E14883F54C066DE204C35D5897E4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020010000000000000000000000C0B16B40C6079B0A317A2FEBC062509CDF447F5C000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060B72403C96F249F0BFF62E9ACFA63D4D35B1647EDD74A4DEF40C06CC450D4E009115052DDC4C710F3449B82322768C443130BAAB9650BEAC29020E616C6B09E89655ADC5595C326EF6CC170160EA778D583B22C4E478CCF528A2167DD27FCAAAF00000000000000000000000000000000000000000000000000000000000000010FACF62512432A91CBE6849BB2CC77B222E417D15FD5005C23B5E39520796041 )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...03d7705Fa
(Beacon Deposit Contract)
34,495,534.286194882010105832 Eth34,495,566.286194882010105832 Eth32
5.129295837252047998 Eth5.129447637252047998 Eth0.0001518
0x91D805fE...Cc9b16628
1.110719702036987693 Eth
Nonce: 108335
1.101641676112415693 Eth
Nonce: 108336
0.009078025924572
0xc0B16b40...CDF447f5c
(Fee Recipient: 0xc0b1...f5c)
10,564.018977074710276704 Eth10,532.018977074710276704 Eth32

Execution Trace

WalletSimple.sendMultiSig( toAddress=0xF0075B3Cf8953d3E23b0eF65960913Fd97eb5227, value=32000000000000000000, data=0x4F498C730000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001A00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030B1473277B9C6BEBCE0C706BD198356A09C73FEBBE7925D85AC43836E412D10629D5E14883F54C066DE204C35D5897E4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020010000000000000000000000C0B16B40C6079B0A317A2FEBC062509CDF447F5C000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060B72403C96F249F0BFF62E9ACFA63D4D35B1647EDD74A4DEF40C06CC450D4E009115052DDC4C710F3449B82322768C443130BAAB9650BEAC29020E616C6B09E89655ADC5595C326EF6CC170160EA778D583B22C4E478CCF528A2167DD27FCAAAF00000000000000000000000000000000000000000000000000000000000000010FACF62512432A91CBE6849BB2CC77B222E417D15FD5005C23B5E39520796041, expireTime=1703116463, sequenceId=993, signature=0x0B8E56A441FF74E678B019F45C3FC45C28969E57B8B2A8ED1C5D5C26ED4B9FB52961648149F953969E9917563D23529F30F94A56DD4AE1B201AE8DC872864E1F1B )
  • WalletSimple.sendMultiSig( toAddress=0xF0075B3Cf8953d3E23b0eF65960913Fd97eb5227, value=32000000000000000000, data=0x4F498C730000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001A00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030B1473277B9C6BEBCE0C706BD198356A09C73FEBBE7925D85AC43836E412D10629D5E14883F54C066DE204C35D5897E4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020010000000000000000000000C0B16B40C6079B0A317A2FEBC062509CDF447F5C000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060B72403C96F249F0BFF62E9ACFA63D4D35B1647EDD74A4DEF40C06CC450D4E009115052DDC4C710F3449B82322768C443130BAAB9650BEAC29020E616C6B09E89655ADC5595C326EF6CC170160EA778D583B22C4E478CCF528A2167DD27FCAAAF00000000000000000000000000000000000000000000000000000000000000010FACF62512432A91CBE6849BB2CC77B222E417D15FD5005C23B5E39520796041, expireTime=1703116463, sequenceId=993, signature=0x0B8E56A441FF74E678B019F45C3FC45C28969E57B8B2A8ED1C5D5C26ED4B9FB52961648149F953969E9917563D23529F30F94A56DD4AE1B201AE8DC872864E1F1B )
    • Null: 0x000...001.a051b4d8( )
    • ETH 32 FigmentEth2Depositor.deposit( )
      • ETH 32 DepositContract.deposit( pubkey=0xB1473277B9C6BEBCE0C706BD198356A09C73FEBBE7925D85AC43836E412D10629D5E14883F54C066DE204C35D5897E48, withdrawal_credentials=0x010000000000000000000000C0B16B40C6079B0A317A2FEBC062509CDF447F5C, signature=0xB72403C96F249F0BFF62E9ACFA63D4D35B1647EDD74A4DEF40C06CC450D4E009115052DDC4C710F3449B82322768C443130BAAB9650BEAC29020E616C6B09E89655ADC5595C326EF6CC170160EA778D583B22C4E478CCF528A2167DD27FCAAAF, deposit_data_root=0FACF62512432A91CBE6849BB2CC77B222E417D15FD5005C23B5E39520796041 )
        • Null: 0x000...002.b1473277( )
        • Null: 0x000...002.b72403c9( )
        • Null: 0x000...002.655adc55( )
        • Null: 0x000...002.7e1d8523( )
        • Null: 0x000...002.b87eefbe( )
        • Null: 0x000...002.00405973( )
        • Null: 0x000...002.824a9267( )
          File 1 of 4: WalletSimple
          {"ERC20Interface.sol":{"content":"// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.7.5;\n\n/**\n * Contract that exposes the needed erc20 token functions\n */\n\nabstract contract ERC20Interface {\n  // Send _value amount of tokens to address _to\n  function transfer(address _to, uint256 _value)\n    public\n    virtual\n    returns (bool success);\n\n  // Get the account balance of another account with address _owner\n  function balanceOf(address _owner)\n    public\n    virtual\n    view\n    returns (uint256 balance);\n}\n"},"Forwarder.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.7.5;\nimport \u0027./TransferHelper.sol\u0027;\nimport \u0027./ERC20Interface.sol\u0027;\n\n/**\n * Contract that will forward any incoming Ether to the creator of the contract\n *\n */\ncontract Forwarder {\n  // Address to which any funds sent to this contract will be forwarded\n  address public parentAddress;\n  event ForwarderDeposited(address from, uint256 value, bytes data);\n\n  /**\n   * Initialize the contract, and sets the destination address to that of the creator\n   */\n  function init(address _parentAddress) external onlyUninitialized {\n    parentAddress = _parentAddress;\n    uint256 value = address(this).balance;\n\n    if (value == 0) {\n      return;\n    }\n\n    (bool success, ) = parentAddress.call{ value: value }(\u0027\u0027);\n    require(success, \u0027Flush failed\u0027);\n    // NOTE: since we are forwarding on initialization,\n    // we don\u0027t have the context of the original sender.\n    // We still emit an event about the forwarding but set\n    // the sender to the forwarder itself\n    emit ForwarderDeposited(address(this), value, msg.data);\n  }\n\n  /**\n   * Modifier that will execute internal code block only if the sender is the parent address\n   */\n  modifier onlyParent {\n    require(msg.sender == parentAddress, \u0027Only Parent\u0027);\n    _;\n  }\n\n  /**\n   * Modifier that will execute internal code block only if the contract has not been initialized yet\n   */\n  modifier onlyUninitialized {\n    require(parentAddress == address(0x0), \u0027Already initialized\u0027);\n    _;\n  }\n\n  /**\n   * Default function; Gets called when data is sent but does not match any other function\n   */\n  fallback() external payable {\n    flush();\n  }\n\n  /**\n   * Default function; Gets called when Ether is deposited with no data, and forwards it to the parent address\n   */\n  receive() external payable {\n    flush();\n  }\n\n  /**\n   * Execute a token transfer of the full balance from the forwarder token to the parent address\n   * @param tokenContractAddress the address of the erc20 token contract\n   */\n  function flushTokens(address tokenContractAddress) external onlyParent {\n    ERC20Interface instance = ERC20Interface(tokenContractAddress);\n    address forwarderAddress = address(this);\n    uint256 forwarderBalance = instance.balanceOf(forwarderAddress);\n    if (forwarderBalance == 0) {\n      return;\n    }\n\n    TransferHelper.safeTransfer(\n      tokenContractAddress,\n      parentAddress,\n      forwarderBalance\n    );\n  }\n\n  /**\n   * Flush the entire balance of the contract to the parent address.\n   */\n  function flush() public {\n    uint256 value = address(this).balance;\n\n    if (value == 0) {\n      return;\n    }\n\n    (bool success, ) = parentAddress.call{ value: value }(\u0027\u0027);\n    require(success, \u0027Flush failed\u0027);\n    emit ForwarderDeposited(msg.sender, value, msg.data);\n  }\n}\n"},"TransferHelper.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\n\npragma solidity \u003e=0.7.5;\n\n// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false\nlibrary TransferHelper {\n    function safeApprove(\n        address token,\n        address to,\n        uint256 value\n    ) internal {\n        // bytes4(keccak256(bytes(\u0027approve(address,uint256)\u0027)));\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));\n        require(\n            success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))),\n            \u0027TransferHelper::safeApprove: approve failed\u0027\n        );\n    }\n\n    function safeTransfer(\n        address token,\n        address to,\n        uint256 value\n    ) internal {\n        // bytes4(keccak256(bytes(\u0027transfer(address,uint256)\u0027)));\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));\n        require(\n            success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))),\n            \u0027TransferHelper::safeTransfer: transfer failed\u0027\n        );\n    }\n\n    function safeTransferFrom(\n        address token,\n        address from,\n        address to,\n        uint256 value\n    ) internal {\n        // bytes4(keccak256(bytes(\u0027transferFrom(address,address,uint256)\u0027)));\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));\n        require(\n            success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))),\n            \u0027TransferHelper::transferFrom: transferFrom failed\u0027\n        );\n    }\n\n    function safeTransferETH(address to, uint256 value) internal {\n        (bool success, ) = to.call{value: value}(new bytes(0));\n        require(success, \u0027TransferHelper::safeTransferETH: ETH transfer failed\u0027);\n    }\n}\n"},"WalletSimple.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.7.5;\nimport \u0027./TransferHelper.sol\u0027;\nimport \u0027./Forwarder.sol\u0027;\nimport \u0027./ERC20Interface.sol\u0027;\n\n/**\n *\n * WalletSimple\n * ============\n *\n * Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds.\n * Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction.\n *\n * The first signature is created on the operation hash (see Data Formats) and passed to sendMultiSig/sendMultiSigToken\n * The signer is determined by verifyMultiSig().\n *\n * The second signature is created by the submitter of the transaction and determined by msg.signer.\n *\n * Data Formats\n * ============\n *\n * The signature is created with ethereumjs-util.ecsign(operationHash).\n * Like the eth_sign RPC call, it packs the values as a 65-byte array of [r, s, v].\n * Unlike eth_sign, the message is not prefixed.\n *\n * The operationHash the result of keccak256(prefix, toAddress, value, data, expireTime).\n * For ether transactions, `prefix` is \"ETHER\".\n * For token transaction, `prefix` is \"ERC20\" and `data` is the tokenContractAddress.\n *\n *\n */\ncontract WalletSimple {\n  // Events\n  event Deposited(address from, uint256 value, bytes data);\n  event SafeModeActivated(address msgSender);\n  event Transacted(\n    address msgSender, // Address of the sender of the message initiating the transaction\n    address otherSigner, // Address of the signer (second signature) used to initiate the transaction\n    bytes32 operation, // Operation hash (see Data Formats)\n    address toAddress, // The address the transaction was sent to\n    uint256 value, // Amount of Wei sent to the address\n    bytes data // Data sent when invoking the transaction\n  );\n\n  event BatchTransfer(address sender, address recipient, uint256 value);\n  // this event shows the other signer and the operation hash that they signed\n  // specific batch transfer events are emitted in Batcher\n  event BatchTransacted(\n    address msgSender, // Address of the sender of the message initiating the transaction\n    address otherSigner, // Address of the signer (second signature) used to initiate the transaction\n    bytes32 operation // Operation hash (see Data Formats)\n  );\n\n  // Public fields\n  mapping(address =\u003e bool) public signers; // The addresses that can co-sign transactions on the wallet\n  bool public safeMode = false; // When active, wallet may only send to signer addresses\n  bool public initialized = false; // True if the contract has been initialized\n\n  // Internal fields\n  uint256 private constant MAX_SEQUENCE_ID_INCREASE = 10000;\n  uint256 constant SEQUENCE_ID_WINDOW_SIZE = 10;\n  uint256[SEQUENCE_ID_WINDOW_SIZE] recentSequenceIds;\n\n  /**\n   * Set up a simple multi-sig wallet by specifying the signers allowed to be used on this wallet.\n   * 2 signers will be required to send a transaction from this wallet.\n   * Note: The sender is NOT automatically added to the list of signers.\n   * Signers CANNOT be changed once they are set\n   *\n   * @param allowedSigners An array of signers on the wallet\n   */\n  function init(address[] calldata allowedSigners) external onlyUninitialized {\n    require(allowedSigners.length == 3, \u0027Invalid number of signers\u0027);\n\n    for (uint8 i = 0; i \u003c allowedSigners.length; i++) {\n      require(allowedSigners[i] != address(0), \u0027Invalid signer\u0027);\n      signers[allowedSigners[i]] = true;\n    }\n    initialized = true;\n  }\n\n  /**\n   * Get the network identifier that signers must sign over\n   * This provides protection signatures being replayed on other chains\n   * This must be a virtual function because chain-specific contracts will need\n   *    to override with their own network ids. It also can\u0027t be a field\n   *    to allow this contract to be used by proxy with delegatecall, which will\n   *    not pick up on state variables\n   */\n  function getNetworkId() internal virtual pure returns (string memory) {\n    return \u0027ETHER\u0027;\n  }\n\n  /**\n   * Get the network identifier that signers must sign over for token transfers\n   * This provides protection signatures being replayed on other chains\n   * This must be a virtual function because chain-specific contracts will need\n   *    to override with their own network ids. It also can\u0027t be a field\n   *    to allow this contract to be used by proxy with delegatecall, which will\n   *    not pick up on state variables\n   */\n  function getTokenNetworkId() internal virtual pure returns (string memory) {\n    return \u0027ERC20\u0027;\n  }\n\n  /**\n   * Get the network identifier that signers must sign over for batch transfers\n   * This provides protection signatures being replayed on other chains\n   * This must be a virtual function because chain-specific contracts will need\n   *    to override with their own network ids. It also can\u0027t be a field\n   *    to allow this contract to be used by proxy with delegatecall, which will\n   *    not pick up on state variables\n   */\n  function getBatchNetworkId() internal virtual pure returns (string memory) {\n    return \u0027ETHER-Batch\u0027;\n  }\n\n  /**\n   * Determine if an address is a signer on this wallet\n   * @param signer address to check\n   * returns boolean indicating whether address is signer or not\n   */\n  function isSigner(address signer) public view returns (bool) {\n    return signers[signer];\n  }\n\n  /**\n   * Modifier that will execute internal code block only if the sender is an authorized signer on this wallet\n   */\n  modifier onlySigner {\n    require(isSigner(msg.sender), \u0027Non-signer in onlySigner method\u0027);\n    _;\n  }\n\n  /**\n   * Modifier that will execute internal code block only if the contract has not been initialized yet\n   */\n  modifier onlyUninitialized {\n    require(!initialized, \u0027Contract already initialized\u0027);\n    _;\n  }\n\n  /**\n   * Gets called when a transaction is received with data that does not match any other method\n   */\n  fallback() external payable {\n    if (msg.value \u003e 0) {\n      // Fire deposited event if we are receiving funds\n      Deposited(msg.sender, msg.value, msg.data);\n    }\n  }\n\n  /**\n   * Gets called when a transaction is received with ether and no data\n   */\n  receive() external payable {\n    if (msg.value \u003e 0) {\n      // Fire deposited event if we are receiving funds\n      Deposited(msg.sender, msg.value, msg.data);\n    }\n  }\n\n  /**\n   * Execute a multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.\n   * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.\n   *\n   * @param toAddress the destination address to send an outgoing transaction\n   * @param value the amount in Wei to be sent\n   * @param data the data to send to the toAddress when invoking the transaction\n   * @param expireTime the number of seconds since 1970 for which this transaction is valid\n   * @param sequenceId the unique sequence id obtainable from getNextSequenceId\n   * @param signature see Data Formats\n   */\n  function sendMultiSig(\n    address toAddress,\n    uint256 value,\n    bytes calldata data,\n    uint256 expireTime,\n    uint256 sequenceId,\n    bytes calldata signature\n  ) external onlySigner {\n    // Verify the other signer\n    bytes32 operationHash = keccak256(\n      abi.encodePacked(\n        getNetworkId(),\n        toAddress,\n        value,\n        data,\n        expireTime,\n        sequenceId\n      )\n    );\n\n    address otherSigner = verifyMultiSig(\n      toAddress,\n      operationHash,\n      signature,\n      expireTime,\n      sequenceId\n    );\n\n    // Success, send the transaction\n    (bool success, ) = toAddress.call{ value: value }(data);\n    require(success, \u0027Call execution failed\u0027);\n\n    emit Transacted(\n      msg.sender,\n      otherSigner,\n      operationHash,\n      toAddress,\n      value,\n      data\n    );\n  }\n\n  /**\n   * Execute a batched multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.\n   * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.\n   * The recipients and values to send are encoded in two arrays, where for index i, recipients[i] will be sent values[i].\n   *\n   * @param recipients The list of recipients to send to\n   * @param values The list of values to send to\n   * @param expireTime the number of seconds since 1970 for which this transaction is valid\n   * @param sequenceId the unique sequence id obtainable from getNextSequenceId\n   * @param signature see Data Formats\n   */\n  function sendMultiSigBatch(\n    address[] calldata recipients,\n    uint256[] calldata values,\n    uint256 expireTime,\n    uint256 sequenceId,\n    bytes calldata signature\n  ) external onlySigner {\n    require(recipients.length != 0, \u0027Not enough recipients\u0027);\n    require(\n      recipients.length == values.length,\n      \u0027Unequal recipients and values\u0027\n    );\n    require(recipients.length \u003c 256, \u0027Too many recipients, max 255\u0027);\n\n    // Verify the other signer\n    bytes32 operationHash = keccak256(\n      abi.encodePacked(\n        getBatchNetworkId(),\n        recipients,\n        values,\n        expireTime,\n        sequenceId\n      )\n    );\n\n    // the first parameter (toAddress) is used to ensure transactions in safe mode only go to a signer\n    // if in safe mode, we should use normal sendMultiSig to recover, so this check will always fail if in safe mode\n    require(!safeMode, \u0027Batch in safe mode\u0027);\n    address otherSigner = verifyMultiSig(\n      address(0x0),\n      operationHash,\n      signature,\n      expireTime,\n      sequenceId\n    );\n\n    batchTransfer(recipients, values);\n    emit BatchTransacted(msg.sender, otherSigner, operationHash);\n  }\n\n  /**\n   * Transfer funds in a batch to each of recipients\n   * @param recipients The list of recipients to send to\n   * @param values The list of values to send to recipients.\n   *  The recipient with index i in recipients array will be sent values[i].\n   *  Thus, recipients and values must be the same length\n   */\n  function batchTransfer(\n    address[] calldata recipients,\n    uint256[] calldata values\n  ) internal {\n    for (uint256 i = 0; i \u003c recipients.length; i++) {\n      require(address(this).balance \u003e= values[i], \u0027Insufficient funds\u0027);\n\n      (bool success, ) = recipients[i].call{ value: values[i] }(\u0027\u0027);\n      require(success, \u0027Call failed\u0027);\n\n      emit BatchTransfer(msg.sender, recipients[i], values[i]);\n    }\n  }\n\n  /**\n   * Execute a multi-signature token transfer from this wallet using 2 signers: one from msg.sender and the other from ecrecover.\n   * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.\n   *\n   * @param toAddress the destination address to send an outgoing transaction\n   * @param value the amount in tokens to be sent\n   * @param tokenContractAddress the address of the erc20 token contract\n   * @param expireTime the number of seconds since 1970 for which this transaction is valid\n   * @param sequenceId the unique sequence id obtainable from getNextSequenceId\n   * @param signature see Data Formats\n   */\n  function sendMultiSigToken(\n    address toAddress,\n    uint256 value,\n    address tokenContractAddress,\n    uint256 expireTime,\n    uint256 sequenceId,\n    bytes calldata signature\n  ) external onlySigner {\n    // Verify the other signer\n    bytes32 operationHash = keccak256(\n      abi.encodePacked(\n        getTokenNetworkId(),\n        toAddress,\n        value,\n        tokenContractAddress,\n        expireTime,\n        sequenceId\n      )\n    );\n\n    verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);\n\n    TransferHelper.safeTransfer(tokenContractAddress, toAddress, value);\n  }\n\n  /**\n   * Execute a token flush from one of the forwarder addresses. This transfer needs only a single signature and can be done by any signer\n   *\n   * @param forwarderAddress the address of the forwarder address to flush the tokens from\n   * @param tokenContractAddress the address of the erc20 token contract\n   */\n  function flushForwarderTokens(\n    address payable forwarderAddress,\n    address tokenContractAddress\n  ) external onlySigner {\n    Forwarder forwarder = Forwarder(forwarderAddress);\n    forwarder.flushTokens(tokenContractAddress);\n  }\n\n  /**\n   * Do common multisig verification for both eth sends and erc20token transfers\n   *\n   * @param toAddress the destination address to send an outgoing transaction\n   * @param operationHash see Data Formats\n   * @param signature see Data Formats\n   * @param expireTime the number of seconds since 1970 for which this transaction is valid\n   * @param sequenceId the unique sequence id obtainable from getNextSequenceId\n   * returns address that has created the signature\n   */\n  function verifyMultiSig(\n    address toAddress,\n    bytes32 operationHash,\n    bytes calldata signature,\n    uint256 expireTime,\n    uint256 sequenceId\n  ) private returns (address) {\n    address otherSigner = recoverAddressFromSignature(operationHash, signature);\n\n    // Verify if we are in safe mode. In safe mode, the wallet can only send to signers\n    require(!safeMode || isSigner(toAddress), \u0027External transfer in safe mode\u0027);\n\n    // Verify that the transaction has not expired\n    require(expireTime \u003e= block.timestamp, \u0027Transaction expired\u0027);\n\n    // Try to insert the sequence ID. Will revert if the sequence id was invalid\n    tryInsertSequenceId(sequenceId);\n\n    require(isSigner(otherSigner), \u0027Invalid signer\u0027);\n\n    require(otherSigner != msg.sender, \u0027Signers cannot be equal\u0027);\n\n    return otherSigner;\n  }\n\n  /**\n   * Irrevocably puts contract into safe mode. When in this mode, transactions may only be sent to signing addresses.\n   */\n  function activateSafeMode() external onlySigner {\n    safeMode = true;\n    SafeModeActivated(msg.sender);\n  }\n\n  /**\n   * Gets signer\u0027s address using ecrecover\n   * @param operationHash see Data Formats\n   * @param signature see Data Formats\n   * returns address recovered from the signature\n   */\n  function recoverAddressFromSignature(\n    bytes32 operationHash,\n    bytes memory signature\n  ) private pure returns (address) {\n    require(signature.length == 65, \u0027Invalid signature - wrong length\u0027);\n\n    // We need to unpack the signature, which is given as an array of 65 bytes (like eth.sign)\n    bytes32 r;\n    bytes32 s;\n    uint8 v;\n\n    // solhint-disable-next-line\n    assembly {\n      r := mload(add(signature, 32))\n      s := mload(add(signature, 64))\n      v := and(mload(add(signature, 65)), 255)\n    }\n    if (v \u003c 27) {\n      v += 27; // Ethereum versions are 27 or 28 as opposed to 0 or 1 which is submitted by some signing libs\n    }\n\n    // protect against signature malleability\n    // S value must be in the lower half orader\n    // reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/051d340171a93a3d401aaaea46b4b62fa81e5d7c/contracts/cryptography/ECDSA.sol#L53\n    require(\n      uint256(s) \u003c=\n        0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,\n      \"ECDSA: invalid signature \u0027s\u0027 value\"\n    );\n\n    // note that this returns 0 if the signature is invalid\n    // Since 0x0 can never be a signer, when the recovered signer address\n    // is checked against our signer list, that 0x0 will cause an invalid signer failure\n    return ecrecover(operationHash, v, r, s);\n  }\n\n  /**\n   * Verify that the sequence id has not been used before and inserts it. Throws if the sequence ID was not accepted.\n   * We collect a window of up to 10 recent sequence ids, and allow any sequence id that is not in the window and\n   * greater than the minimum element in the window.\n   * @param sequenceId to insert into array of stored ids\n   */\n  function tryInsertSequenceId(uint256 sequenceId) private onlySigner {\n    // Keep a pointer to the lowest value element in the window\n    uint256 lowestValueIndex = 0;\n    // fetch recentSequenceIds into memory for function context to avoid unnecessary sloads\n    uint256[SEQUENCE_ID_WINDOW_SIZE] memory _recentSequenceIds = recentSequenceIds;\n    for (uint256 i = 0; i \u003c SEQUENCE_ID_WINDOW_SIZE; i++) {\n      require(_recentSequenceIds[i] != sequenceId, \u0027Sequence ID already used\u0027);\n\n      if (_recentSequenceIds[i] \u003c _recentSequenceIds[lowestValueIndex]) {\n        lowestValueIndex = i;\n      }\n    }\n\n    // The sequence ID being used is lower than the lowest value in the window\n    // so we cannot accept it as it may have been used before\n    require(\n      sequenceId \u003e _recentSequenceIds[lowestValueIndex],\n      \u0027Sequence ID below window\u0027\n    );\n\n    // Block sequence IDs which are much higher than the lowest value\n    // This prevents people blocking the contract by using very large sequence IDs quickly\n    require(\n      sequenceId \u003c=\n        (_recentSequenceIds[lowestValueIndex] + MAX_SEQUENCE_ID_INCREASE),\n      \u0027Sequence ID above maximum\u0027\n    );\n\n    recentSequenceIds[lowestValueIndex] = sequenceId;\n  }\n\n  /**\n   * Gets the next available sequence ID for signing when using executeAndConfirm\n   * returns the sequenceId one higher than the highest currently stored\n   */\n  function getNextSequenceId() public view returns (uint256) {\n    uint256 highestSequenceId = 0;\n    for (uint256 i = 0; i \u003c SEQUENCE_ID_WINDOW_SIZE; i++) {\n      if (recentSequenceIds[i] \u003e highestSequenceId) {\n        highestSequenceId = recentSequenceIds[i];\n      }\n    }\n    return highestSequenceId + 1;\n  }\n}\n"}}

          File 2 of 4: DepositContract
          // ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
          // ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
          // ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
          // ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
          // ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
          // ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
          // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
          // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
          
          // SPDX-License-Identifier: CC0-1.0
          
          pragma solidity 0.6.11;
          
          // This interface is designed to be compatible with the Vyper version.
          /// @notice This is the Ethereum 2.0 deposit contract interface.
          /// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
          interface IDepositContract {
              /// @notice A processed deposit event.
              event DepositEvent(
                  bytes pubkey,
                  bytes withdrawal_credentials,
                  bytes amount,
                  bytes signature,
                  bytes index
              );
          
              /// @notice Submit a Phase 0 DepositData object.
              /// @param pubkey A BLS12-381 public key.
              /// @param withdrawal_credentials Commitment to a public key for withdrawals.
              /// @param signature A BLS12-381 signature.
              /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
              /// Used as a protection against malformed input.
              function deposit(
                  bytes calldata pubkey,
                  bytes calldata withdrawal_credentials,
                  bytes calldata signature,
                  bytes32 deposit_data_root
              ) external payable;
          
              /// @notice Query the current deposit root hash.
              /// @return The deposit root hash.
              function get_deposit_root() external view returns (bytes32);
          
              /// @notice Query the current deposit count.
              /// @return The deposit count encoded as a little endian 64-bit number.
              function get_deposit_count() external view returns (bytes memory);
          }
          
          // Based on official specification in https://eips.ethereum.org/EIPS/eip-165
          interface ERC165 {
              /// @notice Query if a contract implements an interface
              /// @param interfaceId The interface identifier, as specified in ERC-165
              /// @dev Interface identification is specified in ERC-165. This function
              ///  uses less than 30,000 gas.
              /// @return `true` if the contract implements `interfaceId` and
              ///  `interfaceId` is not 0xffffffff, `false` otherwise
              function supportsInterface(bytes4 interfaceId) external pure returns (bool);
          }
          
          // This is a rewrite of the Vyper Eth2.0 deposit contract in Solidity.
          // It tries to stay as close as possible to the original source code.
          /// @notice This is the Ethereum 2.0 deposit contract interface.
          /// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
          contract DepositContract is IDepositContract, ERC165 {
              uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32;
              // NOTE: this also ensures `deposit_count` will fit into 64-bits
              uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1;
          
              bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] branch;
              uint256 deposit_count;
          
              bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] zero_hashes;
          
              constructor() public {
                  // Compute hashes in empty sparse Merkle tree
                  for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1; height++)
                      zero_hashes[height + 1] = sha256(abi.encodePacked(zero_hashes[height], zero_hashes[height]));
              }
          
              function get_deposit_root() override external view returns (bytes32) {
                  bytes32 node;
                  uint size = deposit_count;
                  for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
                      if ((size & 1) == 1)
                          node = sha256(abi.encodePacked(branch[height], node));
                      else
                          node = sha256(abi.encodePacked(node, zero_hashes[height]));
                      size /= 2;
                  }
                  return sha256(abi.encodePacked(
                      node,
                      to_little_endian_64(uint64(deposit_count)),
                      bytes24(0)
                  ));
              }
          
              function get_deposit_count() override external view returns (bytes memory) {
                  return to_little_endian_64(uint64(deposit_count));
              }
          
              function deposit(
                  bytes calldata pubkey,
                  bytes calldata withdrawal_credentials,
                  bytes calldata signature,
                  bytes32 deposit_data_root
              ) override external payable {
                  // Extended ABI length checks since dynamic types are used.
                  require(pubkey.length == 48, "DepositContract: invalid pubkey length");
                  require(withdrawal_credentials.length == 32, "DepositContract: invalid withdrawal_credentials length");
                  require(signature.length == 96, "DepositContract: invalid signature length");
          
                  // Check deposit amount
                  require(msg.value >= 1 ether, "DepositContract: deposit value too low");
                  require(msg.value % 1 gwei == 0, "DepositContract: deposit value not multiple of gwei");
                  uint deposit_amount = msg.value / 1 gwei;
                  require(deposit_amount <= type(uint64).max, "DepositContract: deposit value too high");
          
                  // Emit `DepositEvent` log
                  bytes memory amount = to_little_endian_64(uint64(deposit_amount));
                  emit DepositEvent(
                      pubkey,
                      withdrawal_credentials,
                      amount,
                      signature,
                      to_little_endian_64(uint64(deposit_count))
                  );
          
                  // Compute deposit data root (`DepositData` hash tree root)
                  bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));
                  bytes32 signature_root = sha256(abi.encodePacked(
                      sha256(abi.encodePacked(signature[:64])),
                      sha256(abi.encodePacked(signature[64:], bytes32(0)))
                  ));
                  bytes32 node = sha256(abi.encodePacked(
                      sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
                      sha256(abi.encodePacked(amount, bytes24(0), signature_root))
                  ));
          
                  // Verify computed and expected deposit data roots match
                  require(node == deposit_data_root, "DepositContract: reconstructed DepositData does not match supplied deposit_data_root");
          
                  // Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`)
                  require(deposit_count < MAX_DEPOSIT_COUNT, "DepositContract: merkle tree full");
          
                  // Add deposit data root to Merkle tree (update a single `branch` node)
                  deposit_count += 1;
                  uint size = deposit_count;
                  for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
                      if ((size & 1) == 1) {
                          branch[height] = node;
                          return;
                      }
                      node = sha256(abi.encodePacked(branch[height], node));
                      size /= 2;
                  }
                  // As the loop should always end prematurely with the `return` statement,
                  // this code should be unreachable. We assert `false` just to be safe.
                  assert(false);
              }
          
              function supportsInterface(bytes4 interfaceId) override external pure returns (bool) {
                  return interfaceId == type(ERC165).interfaceId || interfaceId == type(IDepositContract).interfaceId;
              }
          
              function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
                  ret = new bytes(8);
                  bytes8 bytesValue = bytes8(value);
                  // Byteswapping during copying to bytes.
                  ret[0] = bytesValue[7];
                  ret[1] = bytesValue[6];
                  ret[2] = bytesValue[5];
                  ret[3] = bytesValue[4];
                  ret[4] = bytesValue[3];
                  ret[5] = bytesValue[2];
                  ret[6] = bytesValue[1];
                  ret[7] = bytesValue[0];
              }
          }

          File 3 of 4: FigmentEth2Depositor
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "../utils/Context.sol";
          /**
           * @dev Contract module which provides a basic access control mechanism, where
           * there is an account (an owner) that can be granted exclusive access to
           * specific functions.
           *
           * By default, the owner account will be the one that deploys the contract. This
           * can later be changed with {transferOwnership}.
           *
           * This module is used through inheritance. It will make available the modifier
           * `onlyOwner`, which can be applied to your functions to restrict their use to
           * the owner.
           */
          abstract contract Ownable is Context {
              address private _owner;
              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              /**
               * @dev Initializes the contract setting the deployer as the initial owner.
               */
              constructor() {
                  _setOwner(_msgSender());
              }
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view virtual returns (address) {
                  return _owner;
              }
              /**
               * @dev Throws if called by any account other than the owner.
               */
              modifier onlyOwner() {
                  require(owner() == _msgSender(), "Ownable: caller is not the owner");
                  _;
              }
              /**
               * @dev Leaves the contract without owner. It will not be possible to call
               * `onlyOwner` functions anymore. Can only be called by the current owner.
               *
               * NOTE: Renouncing ownership will leave the contract without an owner,
               * thereby removing any functionality that is only available to the owner.
               */
              function renounceOwnership() public virtual onlyOwner {
                  _setOwner(address(0));
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Can only be called by the current owner.
               */
              function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                  _setOwner(newOwner);
              }
              function _setOwner(address newOwner) private {
                  address oldOwner = _owner;
                  _owner = newOwner;
                  emit OwnershipTransferred(oldOwner, newOwner);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "../utils/Context.sol";
          /**
           * @dev Contract module which allows children to implement an emergency stop
           * mechanism that can be triggered by an authorized account.
           *
           * This module is used through inheritance. It will make available the
           * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
           * the functions of your contract. Note that they will not be pausable by
           * simply including this module, only once the modifiers are put in place.
           */
          abstract contract Pausable is Context {
              /**
               * @dev Emitted when the pause is triggered by `account`.
               */
              event Paused(address account);
              /**
               * @dev Emitted when the pause is lifted by `account`.
               */
              event Unpaused(address account);
              bool private _paused;
              /**
               * @dev Initializes the contract in unpaused state.
               */
              constructor() {
                  _paused = false;
              }
              /**
               * @dev Returns true if the contract is paused, and false otherwise.
               */
              function paused() public view virtual returns (bool) {
                  return _paused;
              }
              /**
               * @dev Modifier to make a function callable only when the contract is not paused.
               *
               * Requirements:
               *
               * - The contract must not be paused.
               */
              modifier whenNotPaused() {
                  require(!paused(), "Pausable: paused");
                  _;
              }
              /**
               * @dev Modifier to make a function callable only when the contract is paused.
               *
               * Requirements:
               *
               * - The contract must be paused.
               */
              modifier whenPaused() {
                  require(paused(), "Pausable: not paused");
                  _;
              }
              /**
               * @dev Triggers stopped state.
               *
               * Requirements:
               *
               * - The contract must not be paused.
               */
              function _pause() internal virtual whenNotPaused {
                  _paused = true;
                  emit Paused(_msgSender());
              }
              /**
               * @dev Returns to normal state.
               *
               * Requirements:
               *
               * - The contract must be paused.
               */
              function _unpause() internal virtual whenPaused {
                  _paused = false;
                  emit Unpaused(_msgSender());
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.10;
          import "@openzeppelin/contracts/access/Ownable.sol";
          import "@openzeppelin/contracts/security/Pausable.sol";
          import "../contracts/interfaces/IDepositContract.sol";
          contract FigmentEth2Depositor is Pausable, Ownable {
              /**
               * @dev Eth2 Deposit Contract address.
               */
              IDepositContract public immutable depositContract;
              /**
               * @dev Minimal and maximum amount of nodes per transaction.
               */
              uint256 public constant nodesMinAmount = 1;
              uint256 public constant pubkeyLength = 48;
              uint256 public constant credentialsLength = 32;
              uint256 public constant signatureLength = 96;
              /**
               * @dev Collateral size of one node.
               */
              uint256 public constant collateral = 32 ether;
              /**
               * @dev Setting Eth2 Smart Contract address during construction.
               */
              constructor(address depositContract_) {
                  require(depositContract_ != address(0), "Zero address");
                  depositContract = IDepositContract(depositContract_);
              }
              /**
               * @dev This contract will not accept direct ETH transactions.
               */
              receive() external payable {
                  revert("Do not send ETH here");
              }
              /**
               * @dev Function that allows to deposit many nodes at once.
               *
               * - pubkeys                - Array of BLS12-381 public keys.
               * - withdrawal_credentials - Array of commitments to a public keys for withdrawals.
               * - signatures             - Array of BLS12-381 signatures.
               * - deposit_data_roots     - Array of the SHA-256 hashes of the SSZ-encoded DepositData objects.
               */
              function deposit(
                  bytes[] calldata pubkeys,
                  bytes[] calldata withdrawal_credentials,
                  bytes[] calldata signatures,
                  bytes32[] calldata deposit_data_roots
              ) external payable whenNotPaused {
                  uint256 nodesAmount = pubkeys.length;
                  require(nodesAmount > 0, "1 node min / tx");
                  require(msg.value == collateral * nodesAmount, "ETH amount mismatch");
                  require(
                      withdrawal_credentials.length == nodesAmount &&
                      signatures.length == nodesAmount &&
                      deposit_data_roots.length == nodesAmount,
                      "Parameters mismatch");
                  for (uint256 i; i < nodesAmount; ++i) {
                      require(pubkeys[i].length == pubkeyLength, "Wrong pubkey");
                      require(withdrawal_credentials[i].length == credentialsLength, "Wrong withdrawal cred");
                      require(signatures[i].length == signatureLength, "Wrong signatures");
                      IDepositContract(address(depositContract)).deposit{value: collateral}(
                          pubkeys[i],
                          withdrawal_credentials[i],
                          signatures[i],
                          deposit_data_roots[i]
                      );
                  }
                  emit DepositEvent(msg.sender, nodesAmount);
              }
              /**
               * @dev Triggers stopped state.
               *
               * Requirements:
               *
               * - The contract must not be paused.
               */
              function pause() external onlyOwner {
                _pause();
              }
              /**
               * @dev Returns to normal state.
               *
               * Requirements:
               *
               * - The contract must be paused.
               */
              function unpause() external onlyOwner {
                _unpause();
              }
              event DepositEvent(address from, uint256 nodesAmount);
          }
          // SPDX-License-Identifier: CC0-1.0
          pragma solidity ^0.8.0;
          // This interface is designed to be compatible with the Vyper version.
          /// @notice This is the Ethereum 2.0 deposit contract interface.
          /// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
          interface IDepositContract {
              /// @notice A processed deposit event.
              event DepositEvent(
                  bytes pubkey,
                  bytes withdrawal_credentials,
                  bytes amount,
                  bytes signature,
                  bytes index
              );
              /// @notice Submit a Phase 0 DepositData object.
              /// @param pubkey A BLS12-381 public key.
              /// @param withdrawal_credentials Commitment to a public key for withdrawals.
              /// @param signature A BLS12-381 signature.
              /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
              /// Used as a protection against malformed input.
              function deposit(
                  bytes calldata pubkey,
                  bytes calldata withdrawal_credentials,
                  bytes calldata signature,
                  bytes32 deposit_data_root
              ) external payable;
              /// @notice Query the current deposit root hash.
              /// @return The deposit root hash.
              function get_deposit_root() external view returns (bytes32);
              /// @notice Query the current deposit count.
              /// @return The deposit count encoded as a little endian 64-bit number.
              function get_deposit_count() external view returns (bytes memory);
          }
          

          File 4 of 4: WalletSimple
          {"ERC20Interface.sol":{"content":"// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.7.5;\n\n/**\n * Contract that exposes the needed erc20 token functions\n */\n\nabstract contract ERC20Interface {\n  // Send _value amount of tokens to address _to\n  function transfer(address _to, uint256 _value)\n    public\n    virtual\n    returns (bool success);\n\n  // Get the account balance of another account with address _owner\n  function balanceOf(address _owner)\n    public\n    virtual\n    view\n    returns (uint256 balance);\n}\n"},"Forwarder.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.7.5;\nimport \u0027./TransferHelper.sol\u0027;\nimport \u0027./ERC20Interface.sol\u0027;\n\n/**\n * Contract that will forward any incoming Ether to the creator of the contract\n *\n */\ncontract Forwarder {\n  // Address to which any funds sent to this contract will be forwarded\n  address public parentAddress;\n  event ForwarderDeposited(address from, uint256 value, bytes data);\n\n  /**\n   * Initialize the contract, and sets the destination address to that of the creator\n   */\n  function init(address _parentAddress) external onlyUninitialized {\n    parentAddress = _parentAddress;\n    uint256 value = address(this).balance;\n\n    if (value == 0) {\n      return;\n    }\n\n    (bool success, ) = parentAddress.call{ value: value }(\u0027\u0027);\n    require(success, \u0027Flush failed\u0027);\n    // NOTE: since we are forwarding on initialization,\n    // we don\u0027t have the context of the original sender.\n    // We still emit an event about the forwarding but set\n    // the sender to the forwarder itself\n    emit ForwarderDeposited(address(this), value, msg.data);\n  }\n\n  /**\n   * Modifier that will execute internal code block only if the sender is the parent address\n   */\n  modifier onlyParent {\n    require(msg.sender == parentAddress, \u0027Only Parent\u0027);\n    _;\n  }\n\n  /**\n   * Modifier that will execute internal code block only if the contract has not been initialized yet\n   */\n  modifier onlyUninitialized {\n    require(parentAddress == address(0x0), \u0027Already initialized\u0027);\n    _;\n  }\n\n  /**\n   * Default function; Gets called when data is sent but does not match any other function\n   */\n  fallback() external payable {\n    flush();\n  }\n\n  /**\n   * Default function; Gets called when Ether is deposited with no data, and forwards it to the parent address\n   */\n  receive() external payable {\n    flush();\n  }\n\n  /**\n   * Execute a token transfer of the full balance from the forwarder token to the parent address\n   * @param tokenContractAddress the address of the erc20 token contract\n   */\n  function flushTokens(address tokenContractAddress) external onlyParent {\n    ERC20Interface instance = ERC20Interface(tokenContractAddress);\n    address forwarderAddress = address(this);\n    uint256 forwarderBalance = instance.balanceOf(forwarderAddress);\n    if (forwarderBalance == 0) {\n      return;\n    }\n\n    TransferHelper.safeTransfer(\n      tokenContractAddress,\n      parentAddress,\n      forwarderBalance\n    );\n  }\n\n  /**\n   * Flush the entire balance of the contract to the parent address.\n   */\n  function flush() public {\n    uint256 value = address(this).balance;\n\n    if (value == 0) {\n      return;\n    }\n\n    (bool success, ) = parentAddress.call{ value: value }(\u0027\u0027);\n    require(success, \u0027Flush failed\u0027);\n    emit ForwarderDeposited(msg.sender, value, msg.data);\n  }\n}\n"},"TransferHelper.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\n\npragma solidity \u003e=0.7.5;\n\n// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false\nlibrary TransferHelper {\n    function safeApprove(\n        address token,\n        address to,\n        uint256 value\n    ) internal {\n        // bytes4(keccak256(bytes(\u0027approve(address,uint256)\u0027)));\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));\n        require(\n            success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))),\n            \u0027TransferHelper::safeApprove: approve failed\u0027\n        );\n    }\n\n    function safeTransfer(\n        address token,\n        address to,\n        uint256 value\n    ) internal {\n        // bytes4(keccak256(bytes(\u0027transfer(address,uint256)\u0027)));\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));\n        require(\n            success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))),\n            \u0027TransferHelper::safeTransfer: transfer failed\u0027\n        );\n    }\n\n    function safeTransferFrom(\n        address token,\n        address from,\n        address to,\n        uint256 value\n    ) internal {\n        // bytes4(keccak256(bytes(\u0027transferFrom(address,address,uint256)\u0027)));\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));\n        require(\n            success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))),\n            \u0027TransferHelper::transferFrom: transferFrom failed\u0027\n        );\n    }\n\n    function safeTransferETH(address to, uint256 value) internal {\n        (bool success, ) = to.call{value: value}(new bytes(0));\n        require(success, \u0027TransferHelper::safeTransferETH: ETH transfer failed\u0027);\n    }\n}\n"},"WalletSimple.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.7.5;\nimport \u0027./TransferHelper.sol\u0027;\nimport \u0027./Forwarder.sol\u0027;\nimport \u0027./ERC20Interface.sol\u0027;\n\n/**\n *\n * WalletSimple\n * ============\n *\n * Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds.\n * Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction.\n *\n * The first signature is created on the operation hash (see Data Formats) and passed to sendMultiSig/sendMultiSigToken\n * The signer is determined by verifyMultiSig().\n *\n * The second signature is created by the submitter of the transaction and determined by msg.signer.\n *\n * Data Formats\n * ============\n *\n * The signature is created with ethereumjs-util.ecsign(operationHash).\n * Like the eth_sign RPC call, it packs the values as a 65-byte array of [r, s, v].\n * Unlike eth_sign, the message is not prefixed.\n *\n * The operationHash the result of keccak256(prefix, toAddress, value, data, expireTime).\n * For ether transactions, `prefix` is \"ETHER\".\n * For token transaction, `prefix` is \"ERC20\" and `data` is the tokenContractAddress.\n *\n *\n */\ncontract WalletSimple {\n  // Events\n  event Deposited(address from, uint256 value, bytes data);\n  event SafeModeActivated(address msgSender);\n  event Transacted(\n    address msgSender, // Address of the sender of the message initiating the transaction\n    address otherSigner, // Address of the signer (second signature) used to initiate the transaction\n    bytes32 operation, // Operation hash (see Data Formats)\n    address toAddress, // The address the transaction was sent to\n    uint256 value, // Amount of Wei sent to the address\n    bytes data // Data sent when invoking the transaction\n  );\n\n  event BatchTransfer(address sender, address recipient, uint256 value);\n  // this event shows the other signer and the operation hash that they signed\n  // specific batch transfer events are emitted in Batcher\n  event BatchTransacted(\n    address msgSender, // Address of the sender of the message initiating the transaction\n    address otherSigner, // Address of the signer (second signature) used to initiate the transaction\n    bytes32 operation // Operation hash (see Data Formats)\n  );\n\n  // Public fields\n  mapping(address =\u003e bool) public signers; // The addresses that can co-sign transactions on the wallet\n  bool public safeMode = false; // When active, wallet may only send to signer addresses\n  bool public initialized = false; // True if the contract has been initialized\n\n  // Internal fields\n  uint256 private constant MAX_SEQUENCE_ID_INCREASE = 10000;\n  uint256 constant SEQUENCE_ID_WINDOW_SIZE = 10;\n  uint256[SEQUENCE_ID_WINDOW_SIZE] recentSequenceIds;\n\n  /**\n   * Set up a simple multi-sig wallet by specifying the signers allowed to be used on this wallet.\n   * 2 signers will be required to send a transaction from this wallet.\n   * Note: The sender is NOT automatically added to the list of signers.\n   * Signers CANNOT be changed once they are set\n   *\n   * @param allowedSigners An array of signers on the wallet\n   */\n  function init(address[] calldata allowedSigners) external onlyUninitialized {\n    require(allowedSigners.length == 3, \u0027Invalid number of signers\u0027);\n\n    for (uint8 i = 0; i \u003c allowedSigners.length; i++) {\n      require(allowedSigners[i] != address(0), \u0027Invalid signer\u0027);\n      signers[allowedSigners[i]] = true;\n    }\n    initialized = true;\n  }\n\n  /**\n   * Get the network identifier that signers must sign over\n   * This provides protection signatures being replayed on other chains\n   * This must be a virtual function because chain-specific contracts will need\n   *    to override with their own network ids. It also can\u0027t be a field\n   *    to allow this contract to be used by proxy with delegatecall, which will\n   *    not pick up on state variables\n   */\n  function getNetworkId() internal virtual pure returns (string memory) {\n    return \u0027ETHER\u0027;\n  }\n\n  /**\n   * Get the network identifier that signers must sign over for token transfers\n   * This provides protection signatures being replayed on other chains\n   * This must be a virtual function because chain-specific contracts will need\n   *    to override with their own network ids. It also can\u0027t be a field\n   *    to allow this contract to be used by proxy with delegatecall, which will\n   *    not pick up on state variables\n   */\n  function getTokenNetworkId() internal virtual pure returns (string memory) {\n    return \u0027ERC20\u0027;\n  }\n\n  /**\n   * Get the network identifier that signers must sign over for batch transfers\n   * This provides protection signatures being replayed on other chains\n   * This must be a virtual function because chain-specific contracts will need\n   *    to override with their own network ids. It also can\u0027t be a field\n   *    to allow this contract to be used by proxy with delegatecall, which will\n   *    not pick up on state variables\n   */\n  function getBatchNetworkId() internal virtual pure returns (string memory) {\n    return \u0027ETHER-Batch\u0027;\n  }\n\n  /**\n   * Determine if an address is a signer on this wallet\n   * @param signer address to check\n   * returns boolean indicating whether address is signer or not\n   */\n  function isSigner(address signer) public view returns (bool) {\n    return signers[signer];\n  }\n\n  /**\n   * Modifier that will execute internal code block only if the sender is an authorized signer on this wallet\n   */\n  modifier onlySigner {\n    require(isSigner(msg.sender), \u0027Non-signer in onlySigner method\u0027);\n    _;\n  }\n\n  /**\n   * Modifier that will execute internal code block only if the contract has not been initialized yet\n   */\n  modifier onlyUninitialized {\n    require(!initialized, \u0027Contract already initialized\u0027);\n    _;\n  }\n\n  /**\n   * Gets called when a transaction is received with data that does not match any other method\n   */\n  fallback() external payable {\n    if (msg.value \u003e 0) {\n      // Fire deposited event if we are receiving funds\n      Deposited(msg.sender, msg.value, msg.data);\n    }\n  }\n\n  /**\n   * Gets called when a transaction is received with ether and no data\n   */\n  receive() external payable {\n    if (msg.value \u003e 0) {\n      // Fire deposited event if we are receiving funds\n      Deposited(msg.sender, msg.value, msg.data);\n    }\n  }\n\n  /**\n   * Execute a multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.\n   * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.\n   *\n   * @param toAddress the destination address to send an outgoing transaction\n   * @param value the amount in Wei to be sent\n   * @param data the data to send to the toAddress when invoking the transaction\n   * @param expireTime the number of seconds since 1970 for which this transaction is valid\n   * @param sequenceId the unique sequence id obtainable from getNextSequenceId\n   * @param signature see Data Formats\n   */\n  function sendMultiSig(\n    address toAddress,\n    uint256 value,\n    bytes calldata data,\n    uint256 expireTime,\n    uint256 sequenceId,\n    bytes calldata signature\n  ) external onlySigner {\n    // Verify the other signer\n    bytes32 operationHash = keccak256(\n      abi.encodePacked(\n        getNetworkId(),\n        toAddress,\n        value,\n        data,\n        expireTime,\n        sequenceId\n      )\n    );\n\n    address otherSigner = verifyMultiSig(\n      toAddress,\n      operationHash,\n      signature,\n      expireTime,\n      sequenceId\n    );\n\n    // Success, send the transaction\n    (bool success, ) = toAddress.call{ value: value }(data);\n    require(success, \u0027Call execution failed\u0027);\n\n    emit Transacted(\n      msg.sender,\n      otherSigner,\n      operationHash,\n      toAddress,\n      value,\n      data\n    );\n  }\n\n  /**\n   * Execute a batched multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.\n   * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.\n   * The recipients and values to send are encoded in two arrays, where for index i, recipients[i] will be sent values[i].\n   *\n   * @param recipients The list of recipients to send to\n   * @param values The list of values to send to\n   * @param expireTime the number of seconds since 1970 for which this transaction is valid\n   * @param sequenceId the unique sequence id obtainable from getNextSequenceId\n   * @param signature see Data Formats\n   */\n  function sendMultiSigBatch(\n    address[] calldata recipients,\n    uint256[] calldata values,\n    uint256 expireTime,\n    uint256 sequenceId,\n    bytes calldata signature\n  ) external onlySigner {\n    require(recipients.length != 0, \u0027Not enough recipients\u0027);\n    require(\n      recipients.length == values.length,\n      \u0027Unequal recipients and values\u0027\n    );\n    require(recipients.length \u003c 256, \u0027Too many recipients, max 255\u0027);\n\n    // Verify the other signer\n    bytes32 operationHash = keccak256(\n      abi.encodePacked(\n        getBatchNetworkId(),\n        recipients,\n        values,\n        expireTime,\n        sequenceId\n      )\n    );\n\n    // the first parameter (toAddress) is used to ensure transactions in safe mode only go to a signer\n    // if in safe mode, we should use normal sendMultiSig to recover, so this check will always fail if in safe mode\n    require(!safeMode, \u0027Batch in safe mode\u0027);\n    address otherSigner = verifyMultiSig(\n      address(0x0),\n      operationHash,\n      signature,\n      expireTime,\n      sequenceId\n    );\n\n    batchTransfer(recipients, values);\n    emit BatchTransacted(msg.sender, otherSigner, operationHash);\n  }\n\n  /**\n   * Transfer funds in a batch to each of recipients\n   * @param recipients The list of recipients to send to\n   * @param values The list of values to send to recipients.\n   *  The recipient with index i in recipients array will be sent values[i].\n   *  Thus, recipients and values must be the same length\n   */\n  function batchTransfer(\n    address[] calldata recipients,\n    uint256[] calldata values\n  ) internal {\n    for (uint256 i = 0; i \u003c recipients.length; i++) {\n      require(address(this).balance \u003e= values[i], \u0027Insufficient funds\u0027);\n\n      (bool success, ) = recipients[i].call{ value: values[i] }(\u0027\u0027);\n      require(success, \u0027Call failed\u0027);\n\n      emit BatchTransfer(msg.sender, recipients[i], values[i]);\n    }\n  }\n\n  /**\n   * Execute a multi-signature token transfer from this wallet using 2 signers: one from msg.sender and the other from ecrecover.\n   * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.\n   *\n   * @param toAddress the destination address to send an outgoing transaction\n   * @param value the amount in tokens to be sent\n   * @param tokenContractAddress the address of the erc20 token contract\n   * @param expireTime the number of seconds since 1970 for which this transaction is valid\n   * @param sequenceId the unique sequence id obtainable from getNextSequenceId\n   * @param signature see Data Formats\n   */\n  function sendMultiSigToken(\n    address toAddress,\n    uint256 value,\n    address tokenContractAddress,\n    uint256 expireTime,\n    uint256 sequenceId,\n    bytes calldata signature\n  ) external onlySigner {\n    // Verify the other signer\n    bytes32 operationHash = keccak256(\n      abi.encodePacked(\n        getTokenNetworkId(),\n        toAddress,\n        value,\n        tokenContractAddress,\n        expireTime,\n        sequenceId\n      )\n    );\n\n    verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);\n\n    TransferHelper.safeTransfer(tokenContractAddress, toAddress, value);\n  }\n\n  /**\n   * Execute a token flush from one of the forwarder addresses. This transfer needs only a single signature and can be done by any signer\n   *\n   * @param forwarderAddress the address of the forwarder address to flush the tokens from\n   * @param tokenContractAddress the address of the erc20 token contract\n   */\n  function flushForwarderTokens(\n    address payable forwarderAddress,\n    address tokenContractAddress\n  ) external onlySigner {\n    Forwarder forwarder = Forwarder(forwarderAddress);\n    forwarder.flushTokens(tokenContractAddress);\n  }\n\n  /**\n   * Do common multisig verification for both eth sends and erc20token transfers\n   *\n   * @param toAddress the destination address to send an outgoing transaction\n   * @param operationHash see Data Formats\n   * @param signature see Data Formats\n   * @param expireTime the number of seconds since 1970 for which this transaction is valid\n   * @param sequenceId the unique sequence id obtainable from getNextSequenceId\n   * returns address that has created the signature\n   */\n  function verifyMultiSig(\n    address toAddress,\n    bytes32 operationHash,\n    bytes calldata signature,\n    uint256 expireTime,\n    uint256 sequenceId\n  ) private returns (address) {\n    address otherSigner = recoverAddressFromSignature(operationHash, signature);\n\n    // Verify if we are in safe mode. In safe mode, the wallet can only send to signers\n    require(!safeMode || isSigner(toAddress), \u0027External transfer in safe mode\u0027);\n\n    // Verify that the transaction has not expired\n    require(expireTime \u003e= block.timestamp, \u0027Transaction expired\u0027);\n\n    // Try to insert the sequence ID. Will revert if the sequence id was invalid\n    tryInsertSequenceId(sequenceId);\n\n    require(isSigner(otherSigner), \u0027Invalid signer\u0027);\n\n    require(otherSigner != msg.sender, \u0027Signers cannot be equal\u0027);\n\n    return otherSigner;\n  }\n\n  /**\n   * Irrevocably puts contract into safe mode. When in this mode, transactions may only be sent to signing addresses.\n   */\n  function activateSafeMode() external onlySigner {\n    safeMode = true;\n    SafeModeActivated(msg.sender);\n  }\n\n  /**\n   * Gets signer\u0027s address using ecrecover\n   * @param operationHash see Data Formats\n   * @param signature see Data Formats\n   * returns address recovered from the signature\n   */\n  function recoverAddressFromSignature(\n    bytes32 operationHash,\n    bytes memory signature\n  ) private pure returns (address) {\n    require(signature.length == 65, \u0027Invalid signature - wrong length\u0027);\n\n    // We need to unpack the signature, which is given as an array of 65 bytes (like eth.sign)\n    bytes32 r;\n    bytes32 s;\n    uint8 v;\n\n    // solhint-disable-next-line\n    assembly {\n      r := mload(add(signature, 32))\n      s := mload(add(signature, 64))\n      v := and(mload(add(signature, 65)), 255)\n    }\n    if (v \u003c 27) {\n      v += 27; // Ethereum versions are 27 or 28 as opposed to 0 or 1 which is submitted by some signing libs\n    }\n\n    // protect against signature malleability\n    // S value must be in the lower half orader\n    // reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/051d340171a93a3d401aaaea46b4b62fa81e5d7c/contracts/cryptography/ECDSA.sol#L53\n    require(\n      uint256(s) \u003c=\n        0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,\n      \"ECDSA: invalid signature \u0027s\u0027 value\"\n    );\n\n    // note that this returns 0 if the signature is invalid\n    // Since 0x0 can never be a signer, when the recovered signer address\n    // is checked against our signer list, that 0x0 will cause an invalid signer failure\n    return ecrecover(operationHash, v, r, s);\n  }\n\n  /**\n   * Verify that the sequence id has not been used before and inserts it. Throws if the sequence ID was not accepted.\n   * We collect a window of up to 10 recent sequence ids, and allow any sequence id that is not in the window and\n   * greater than the minimum element in the window.\n   * @param sequenceId to insert into array of stored ids\n   */\n  function tryInsertSequenceId(uint256 sequenceId) private onlySigner {\n    // Keep a pointer to the lowest value element in the window\n    uint256 lowestValueIndex = 0;\n    // fetch recentSequenceIds into memory for function context to avoid unnecessary sloads\n    uint256[SEQUENCE_ID_WINDOW_SIZE] memory _recentSequenceIds = recentSequenceIds;\n    for (uint256 i = 0; i \u003c SEQUENCE_ID_WINDOW_SIZE; i++) {\n      require(_recentSequenceIds[i] != sequenceId, \u0027Sequence ID already used\u0027);\n\n      if (_recentSequenceIds[i] \u003c _recentSequenceIds[lowestValueIndex]) {\n        lowestValueIndex = i;\n      }\n    }\n\n    // The sequence ID being used is lower than the lowest value in the window\n    // so we cannot accept it as it may have been used before\n    require(\n      sequenceId \u003e _recentSequenceIds[lowestValueIndex],\n      \u0027Sequence ID below window\u0027\n    );\n\n    // Block sequence IDs which are much higher than the lowest value\n    // This prevents people blocking the contract by using very large sequence IDs quickly\n    require(\n      sequenceId \u003c=\n        (_recentSequenceIds[lowestValueIndex] + MAX_SEQUENCE_ID_INCREASE),\n      \u0027Sequence ID above maximum\u0027\n    );\n\n    recentSequenceIds[lowestValueIndex] = sequenceId;\n  }\n\n  /**\n   * Gets the next available sequence ID for signing when using executeAndConfirm\n   * returns the sequenceId one higher than the highest currently stored\n   */\n  function getNextSequenceId() public view returns (uint256) {\n    uint256 highestSequenceId = 0;\n    for (uint256 i = 0; i \u003c SEQUENCE_ID_WINDOW_SIZE; i++) {\n      if (recentSequenceIds[i] \u003e highestSequenceId) {\n        highestSequenceId = recentSequenceIds[i];\n      }\n    }\n    return highestSequenceId + 1;\n  }\n}\n"}}