ETH Price: $2,467.70 (+0.59%)

Transaction Decoder

Block:
20014219 at Jun-03-2024 10:17:11 PM +UTC
Transaction Fee:
0.00100121788022602 ETH $2.47
Gas Used:
120,122 Gas / 8.33500841 Gwei

Emitted Events:

192 0xa8b0ea38892f2d0aab5171b4da3760410162fe09.0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0( 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000c9aad2fc90eddacf02bd11fa690e3b44e318aa9b )
193 CustodialWalletFactoryV2.Created( addr=0xa8b0ea38892f2d0aab5171b4da3760410162fe09 )
194 CustodialWalletFactoryV2.WalletDetails( addr=0xa8b0ea38892f2d0aab5171b4da3760410162fe09, owner=[Sender] 0xc9aad2fc90eddacf02bd11fa690e3b44e318aa9b, index=17171 )

Account State Difference:

  Address   Before After State Difference Code
(beaverbuild)
20.294983262454070743 Eth20.295003932099107187 Eth0.000020669645036444
0xA2A1BF5a...7163CB613
0xA8b0Ea38...10162fe09 From: 0 To: 497590261154554171967157108364460326232086807639687109440905367544123331441611072938796564717714789600418803
0xC9AAd2Fc...4e318AA9b
4.525186851410937317 Eth
Nonce: 20295
4.524185633530711297 Eth
Nonce: 20296
0.00100121788022602

Execution Trace

CustodialWalletFactoryV2.createBatch( owner=0xC9AAd2Fc90EddACF02bD11fa690e3B44e318AA9b, index=[17171] )
  • 0xa8b0ea38892f2d0aab5171b4da3760410162fe09.3d602d80( )
  • 0xa8b0ea38892f2d0aab5171b4da3760410162fe09.19ab453c( )
    • 0x87ae9be718aa310323042ab9806bd6692c1129b7.19ab453c( )
      pragma solidity ^0.8.7;
      
      // SPDX-License-Identifier: MIT
      
      /*
       * @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) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
      
      
      /**
       * @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 () {
              address msgSender = _msgSender();
              _owner = msgSender;
              emit OwnershipTransferred(address(0), 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 {
              emit OwnershipTransferred(_owner, address(0));
              _owner = 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");
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      }
      
      
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
      
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
      
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
      
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
      
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
      
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
      
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
      
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      
      
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * https://eips.ethereum.org/EIPS/eip-165[EIP].
       *
       * Implementers can declare support of contract interfaces, which can then be
       * queried by others ({ERC165Checker}).
       *
       * For an implementation, see {ERC165}.
       */
      interface IERC165 {
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      
      
      /**
       * @dev Required interface of an ERC1155 compliant contract, as defined in the
       * https://eips.ethereum.org/EIPS/eip-1155[EIP].
       *
       * _Available since v3.1._
       */
      interface IERC1155 is IERC165 {
          /**
           * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
           */
          event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
      
          /**
           * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
           * transfers.
           */
          event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
      
          /**
           * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
           * `approved`.
           */
          event ApprovalForAll(address indexed account, address indexed operator, bool approved);
      
          /**
           * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
           *
           * If an {URI} event was emitted for `id`, the standard
           * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
           * returned by {IERC1155MetadataURI-uri}.
           */
          event URI(string value, uint256 indexed id);
      
          /**
           * @dev Returns the amount of tokens of token type `id` owned by `account`.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function balanceOf(address account, uint256 id) external view returns (uint256);
      
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
           *
           * Requirements:
           *
           * - `accounts` and `ids` must have the same length.
           */
          function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
      
          /**
           * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
           *
           * Emits an {ApprovalForAll} event.
           *
           * Requirements:
           *
           * - `operator` cannot be the caller.
           */
          function setApprovalForAll(address operator, bool approved) external;
      
          /**
           * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
           *
           * See {setApprovalForAll}.
           */
          function isApprovedForAll(address account, address operator) external view returns (bool);
      
          /**
           * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
           *
           * Emits a {TransferSingle} event.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
           * - `from` must have a balance of tokens of type `id` of at least `amount`.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
           * acceptance magic value.
           */
          function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
      
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
           *
           * Emits a {TransferBatch} event.
           *
           * Requirements:
           *
           * - `ids` and `amounts` must have the same length.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
           * acceptance magic value.
           */
          function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
      }
      
      
      /**
       * @dev Required interface of an ERC721 compliant contract.
       */
      interface IERC721 is IERC165 {
          /**
           * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
           */
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
      
          /**
           * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
           */
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
      
          /**
           * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
           */
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
      
          /**
           * @dev Returns the number of tokens in ``owner``'s account.
           */
          function balanceOf(address owner) external view returns (uint256 balance);
      
          /**
           * @dev Returns the owner of the `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function ownerOf(uint256 tokenId) external view returns (address owner);
      
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
           * are aware of the ERC721 protocol to prevent tokens from being forever locked.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(address from, address to, uint256 tokenId) external payable;
      
          /**
           * @dev Transfers `tokenId` token from `from` to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address from, address to, uint256 tokenId) external;
      
          /**
           * @dev Gives permission to `to` to transfer `tokenId` token to another account.
           * The approval is cleared when the token is transferred.
           *
           * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
           *
           * Requirements:
           *
           * - The caller must own the token or be an approved operator.
           * - `tokenId` must exist.
           *
           * Emits an {Approval} event.
           */
          function approve(address to, uint256 tokenId) external;
      
          /**
           * @dev Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
      
          /**
           * @dev Approve or remove `operator` as an operator for the caller.
           * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
           *
           * Requirements:
           *
           * - The `operator` cannot be the caller.
           *
           * Emits an {ApprovalForAll} event.
           */
          function setApprovalForAll(address operator, bool _approved) external;
      
          /**
           * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
           *
           * See {setApprovalForAll}
           */
          function isApprovedForAll(address owner, address operator) external view returns (bool);
      
          /**
            * @dev Safely transfers `tokenId` token from `from` to `to`.
            *
            * Requirements:
            *
            * - `from` cannot be the zero address.
            * - `to` cannot be the zero address.
            * - `tokenId` token must exist and be owned by `from`.
            * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
            * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
            *
            * Emits a {Transfer} event.
            */
          function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external payable;
      }
      
      
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
      
              uint256 size;
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
      
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
      
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
      
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: value }(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.staticcall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
      
          function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
      
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      
      
      // solhint-disable-next-line compiler-version
      /**
       * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
       * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
       * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
       * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
       *
       * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
       * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
       *
       * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
       * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
       */
      abstract contract Initializable {
      
          /**
           * @dev Indicates that the contract has been initialized.
           */
          bool private _initialized;
      
          /**
           * @dev Indicates that the contract is in the process of being initialized.
           */
          bool private _initializing;
      
          /**
           * @dev Modifier to protect an initializer function from being invoked twice.
           */
          modifier initializer() {
              require(_initializing || !_initialized, "Initializable: contract is already initialized");
      
              bool isTopLevelCall = !_initializing;
              if (isTopLevelCall) {
                  _initializing = true;
                  _initialized = true;
              }
      
              _;
      
              if (isTopLevelCall) {
                  _initializing = false;
              }
          }
      }
      
      
      /**
       * @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 CustodialOwnable is Context, Initializable {
          address private _owner;
      
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          function init(address addr) public virtual initializer  {
              _owner = addr;
              emit OwnershipTransferred(address(0), addr);
          }
      
          /**
           * @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 {
              emit OwnershipTransferred(_owner, address(0));
              _owner = 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");
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      }
      
      
      /**
       * @title SafeERC20
       * @dev Wrappers around ERC20 operations that throw on failure (when the token
       * contract returns false). Tokens that return no value (and instead revert or
       * throw on failure) are also supported, non-reverting calls are assumed to be
       * successful.
       * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
       * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
       */
      library SafeERC20 {
          using Address for address;
      
          function safeTransfer(IERC20 token, address to, uint256 value) internal {
              _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
          }
      
          function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
              _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
          }
      
          /**
           * @dev Deprecated. This function has issues similar to the ones found in
           * {IERC20-approve}, and its usage is discouraged.
           *
           * Whenever possible, use {safeIncreaseAllowance} and
           * {safeDecreaseAllowance} instead.
           */
          function safeApprove(IERC20 token, address spender, uint256 value) internal {
              // safeApprove should only be called when setting an initial allowance,
              // or when resetting it to zero. To increase and decrease it, use
              // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
              // solhint-disable-next-line max-line-length
              require((value == 0) || (token.allowance(address(this), spender) == 0),
                  "SafeERC20: approve from non-zero to non-zero allowance"
              );
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
          }
      
          function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
              uint256 newAllowance = token.allowance(address(this), spender) + value;
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
          }
      
          function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
              unchecked {
                  uint256 oldAllowance = token.allowance(address(this), spender);
                  require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                  uint256 newAllowance = oldAllowance - value;
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
          }
      
          /**
           * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
           * on the return value: the return value is optional (but if data is returned, it must not be false).
           * @param token The token targeted by the call.
           * @param data The call data (encoded using abi.encode or one of its variants).
           */
          function _callOptionalReturn(IERC20 token, bytes memory data) private {
              // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
              // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
              // the target address contains contract code and also asserts for success in the low-level call.
      
              bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
              if (returndata.length > 0) { // Return data is optional
                  // solhint-disable-next-line max-line-length
                  require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
              }
          }
      }
      
      
      contract CustodialWallet is CustodialOwnable {
      
          using SafeERC20 for IERC20;
      
          bool private _locked = false;
          modifier entrancyGuard() {
              require(!_locked, "Reentrant call");
              _locked = true;
              _;
              _locked = false;
          }
      
          event TransferNativeAsset(address indexed recipient, uint256 indexed amount);
      
          function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
              return this.onERC721Received.selector;
          }
      
          function onERC1155Received(address, address, uint256, uint256, bytes memory) public virtual returns (bytes4) {
              return this.onERC1155Received.selector;
          }
      
          function onERC1155BatchReceived(address, address, uint256[] memory, uint256[] memory, bytes memory) public virtual returns (bytes4) {
              return this.onERC1155BatchReceived.selector;
          }
      
          receive() external payable {
          }
      
          function init(address owner) public override {
              CustodialOwnable.init(owner);
          }
      
          /**
              Function transfer assets owned by this wallet to the recipient. Transfer only 1 type of asset.
              @param tokenAddress - address of the asset to own, if transferring native asset, use 0x0000000 address
              @param contractType - type of asset
                                      - 0 - ERC20
                                      - 1 - ERC721
                                      - 2 - ERC1155
                                      - 3 - native asset
              @param recipient - recipient of the transaction
              @param amount - amount to be transferred in the asset based of the contractType, for ERC721 not important
              @param tokenId - tokenId to transfer, valid only for ERC721 and ERC1155
          **/
          function transfer(address tokenAddress, uint256 contractType, address recipient, uint256 amount, uint256 tokenId) public payable onlyOwner entrancyGuard {
              require(recipient != address(0), "Invalid recipient address");
              if (contractType == 0) {
                  IERC20(tokenAddress).safeTransfer(recipient, amount);
              } else if (contractType == 1) {
                  IERC721(tokenAddress).safeTransferFrom(address(this), recipient, tokenId, "");
              } else if (contractType == 2) {
                  IERC1155(tokenAddress).safeTransferFrom(address(this), recipient, tokenId, amount, "");
              } else if (contractType == 3) {
                  Address.sendValue(payable(recipient), amount);
                  emit TransferNativeAsset(recipient, amount);
              } else {
                  revert("Unsupported contract type");
              }
          }
      
          /**
              Function transfer assets owned by this wallet to the recipient. Transfer any number of assets.
              @param tokenAddress - address of the asset to own, if transferring native asset, use 0x0000000 address
              @param contractType - type of asset
                                      - 0 - ERC20
                                      - 1 - ERC721
                                      - 2 - ERC1155
                                      - 3 - native asset
              @param recipient - recipient of the transaction
              @param amount - amount to be transferred in the asset based of the contractType, for ERC721 not important
              @param tokenId - tokenId to transfer, valid only for ERC721 and ERC1155
          **/
          function transferBatch(address[] memory tokenAddress, uint256[] memory contractType, address[] memory recipient, uint256[] memory amount, uint256[] memory tokenId) external payable onlyOwner entrancyGuard{
              require(tokenAddress.length == contractType.length, "Length mismatch between tokenAddress array and contractType array");
              require(recipient.length == contractType.length, "Length mismatch between recipient array and contractType array");
              require(recipient.length == amount.length, "Length mismatch between recipient array and amount array");
              require(amount.length == tokenId.length, "Length mismatch between toeknId array and amount array");
              for (uint256 i = 0; i < tokenAddress.length; i++) {
                  require(recipient[i] != address(0), "Check addresses for validity");
                  if (contractType[i] == 0) {
                      IERC20(tokenAddress[i]).safeTransfer(recipient[i], amount[i]);
                  } else if (contractType[i] == 1) {
                      IERC721(tokenAddress[i]).safeTransferFrom(address(this), recipient[i], tokenId[i], "");
                  } else if (contractType[i] == 2) {
                      IERC1155(tokenAddress[i]).safeTransferFrom(address(this), recipient[i], tokenId[i], amount[i], "");
                  } else if (contractType[i] == 3) {
                      Address.sendValue(payable(recipient[i]), amount[i]);
                      emit TransferNativeAsset(recipient[i], amount[i]);
                  } else {
                      revert("Unsupported contract type");
                  }
              }
          }
      
          /**
              Function approves the transfer of assets owned by this wallet to the spender. Approve only 1 type of asset.
              @param tokenAddress - address of the asset to approve
              @param contractType - type of asset
                                      - 0 - ERC20
                                      - 1 - ERC721
                                      - 2 - ERC1155
              @param spender - who will be able to spend the assets on behalf of the user
              @param amount - amount to be approved to spend in the asset based of the contractType
              @param tokenId - tokenId to transfer, valid only for ERC721 and ERC1155
          **/
          function approve(address tokenAddress, uint256 contractType, address spender, uint256 amount, uint256 tokenId) public virtual onlyOwner {
              if (contractType == 0) {
                  bool approval = IERC20(tokenAddress).approve(spender, amount);
                  require(approval, "Approval was unsuccessful");
              } else if (contractType == 1) {
                  IERC721(tokenAddress).approve(spender, tokenId);
              } else if (contractType == 2) {
                  IERC1155(tokenAddress).setApprovalForAll(spender, true);
              } else {
                  revert("Unsupported contract type");
              }
          }
      }
      
      
      /**
       * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
       * deploying minimal proxy contracts, also known as "clones".
       *
       * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
       * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
       *
       * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
       * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
       * deterministic method.
       *
       * _Available since v3.4._
       */
      library Clones {
          /**
           * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
           *
           * This function uses the create opcode, which should never revert.
           */
          function clone(address implementation) internal returns (address instance) {
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  let ptr := mload(0x40)
                  mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                  mstore(add(ptr, 0x14), shl(0x60, implementation))
                  mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
                  instance := create(0, ptr, 0x37)
              }
              require(instance != address(0), "ERC1167: create failed");
          }
      
          /**
           * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
           *
           * This function uses the create2 opcode and a `salt` to deterministically deploy
           * the clone. Using the same `implementation` and `salt` multiple time will revert, since
           * the clones cannot be deployed twice at the same address.
           */
          function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  let ptr := mload(0x40)
                  mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                  mstore(add(ptr, 0x14), shl(0x60, implementation))
                  mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
                  instance := create2(0, ptr, 0x37, salt)
              }
              require(instance != address(0), "ERC1167: create2 failed");
          }
      
          /**
           * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
           */
          function predictDeterministicAddress(address implementation, bytes32 salt, address deployer) internal pure returns (address predicted) {
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  let ptr := mload(0x40)
                  mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                  mstore(add(ptr, 0x14), shl(0x60, implementation))
                  mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
                  mstore(add(ptr, 0x38), shl(0x60, deployer))
                  mstore(add(ptr, 0x4c), salt)
                  mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
                  predicted := keccak256(add(ptr, 0x37), 0x55)
              }
          }
      
          function predictDeterministicTronAddress(address implementation, bytes32 salt, address deployer) internal pure returns (address predicted) {
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  let ptr := mload(0x40)
                  mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                  mstore(add(ptr, 0x14), shl(0x60, implementation))
                  mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf34100000000000000000000000000000000)
                  mstore(add(ptr, 0x38), shl(0x60, deployer))
                  mstore(add(ptr, 0x4c), salt)
                  mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
                  predicted := keccak256(add(ptr, 0x37), 0x55)
              }
          }
      
          /**
           * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
           */
          function predictDeterministicAddress(address implementation, bytes32 salt) internal view returns (address predicted) {
              return predictDeterministicAddress(implementation, salt, address(this));
          }
      
          function predictDeterministicTronAddress(address implementation, bytes32 salt) internal view returns (address predicted) {
              return predictDeterministicTronAddress(implementation, salt, address(this));
          }
      }
      
      
      contract CustodialWalletFactoryV2 {
      
          using Clones for CustodialWalletFactoryV2;
      
          uint256 private constant _MAX_ARRAY_BOUNDS = 2000;
          uint256 private constant _MAX_ARRAY_CALCULATE_BOUNDS = 10_000;
      
          CustodialWallet private rawWallet;
      
          mapping(bytes32 => address) public wallets;
      
          event WalletDetails(address addr, address owner, uint256 index);
          event Created(address addr);
          event CreateFailed(address addr, address owner, string reason);
      
          constructor () {
              rawWallet = new CustodialWallet();
          }
      
          function getWallet(address owner, uint256 index) public view returns (address addr, bool exists, bytes32 salt) {
              salt = keccak256(abi.encodePacked(owner, index));
              addr = Clones.predictDeterministicAddress(address(rawWallet), salt);
              exists = wallets[salt] != address(0);
          }
      
          function getWallets(address owner, uint256[] memory index) external view returns (address[] memory, bool[] memory, bytes32[] memory) {
              require(index.length <= _MAX_ARRAY_CALCULATE_BOUNDS, "Maximum allowable size of array has been exceeded");
              address[] memory addr = new address[](index.length); 
              bool[] memory exists = new bool[](index.length); 
              bytes32[] memory salt = new bytes32[](index.length);
              
              for (uint256 i = 0; i < index.length; i++) {
                  salt[i] = keccak256(abi.encodePacked(owner, index[i]));
                  addr[i] = Clones.predictDeterministicAddress(address(rawWallet), salt[i]);
                  exists[i] = wallets[salt[i]] != address(0);
              }
              return (addr, exists, salt);
          }
      
          function createBatch(address owner, uint256[] memory index) external {
              require(index.length <= _MAX_ARRAY_BOUNDS, "Maximum allowable size of array has been exceeded");
              for (uint256 i = 0; i < index.length; i++) {
                  (address calculatedAddress, bool exists, bytes32 salt) = getWallet(owner, index[i]);
                  if(exists) {
                      emit CreateFailed(calculatedAddress, owner, "Wallet already exists");
                      continue;
                  }
                  address addr = Clones.cloneDeterministic(address(rawWallet), salt);
                  if(addr != calculatedAddress) {
                      emit CreateFailed(calculatedAddress, owner, "Address doesnt match with predicted address.");
                      continue;
                  }
      
                  wallets[salt] = addr;
                  CustodialWallet(payable(addr)).init(owner);
                  emit Created(addr);
                  emit WalletDetails(addr, owner, index[i]);
              }
          }
      
          function create(address owner, uint256 index) external {
              (address calculatedAddress, bool exists, bytes32 salt) = getWallet(owner, index);
              require(!exists, "Wallet already exists");
              address addr = Clones.cloneDeterministic(address(rawWallet), salt);
              require(addr == calculatedAddress, "Address doesnt match with predicted address.");
      
              wallets[salt] = addr;
              CustodialWallet(payable(addr)).init(owner);
              emit Created(addr);
              emit WalletDetails(addr, owner, index);
          }
      }