// File: BatchTransfer.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC165.sol";
import "./IERC1155.sol";
import "./IERC721.sol";
import "./IERC721Enumerable.sol";

/**
 * Simple contract to enable batched transfers of ERC721 and ERC1155 collections between addresses.
 */
contract BatchTransfer {

    event BatchTransferred(uint256 indexed tokensTransferred, uint256 indexed lastTokenTransferred);

    bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;

    /**
     * Transfers all of the given tokens in this collection between addresses.
     */ 
    function batchTransferAllERC1155(address collection, uint256 tokenId, address destination) external {
        IERC1155 erc1155 = IERC1155(collection);
        erc1155.safeTransferFrom(msg.sender, destination, tokenId, erc1155.balanceOf(msg.sender, tokenId), "");
    }

    /**
     * Transfers all tokens for each given token id.
     */ 
    function batchTransferAllForTokenIdsERC1155(address collection, uint256[] calldata tokenIds, address destination) external {
        IERC1155 erc1155 = IERC1155(collection);
        address[] memory owners = new address[](tokenIds.length);
        for (uint256 i = 0; i < tokenIds.length; i++) {
            owners[i] = msg.sender;
        }

        uint256[] memory amounts = erc1155.balanceOfBatch(owners, tokenIds);
        erc1155.safeBatchTransferFrom(msg.sender, destination, tokenIds, amounts, "");
    }

    /**
     * Transfers the specified amount of the given tokens in this collection between addresses.
     */ 
    function batchTransferERC1155(address collection, uint256 tokenId, uint256 tokenValue, address destination) external {
        IERC1155 erc1155 = IERC1155(collection);
        erc1155.safeTransferFrom(msg.sender, destination, tokenId, tokenValue, "");
    }

    /**
     * Transfers all items in this collection which are owned by the caller to the destination address.
     */
    function batchTransferAllERC721(address collection, address destination) external {
        batchTransferSomeERC721(collection, destination, 0, type(uint256).max);
    }

    /**
     * Transfers all items in this collection which are owned by the caller to the destination address.
     *
     * If the contract implements ERC721Enumerable this call will directly look up the tokens for the calling
     * address.  Otherwise it will search all tokens, optionally starting at `startID`.
     */
    function batchTransferSomeERC721(address collection, address destination, uint256 startId, uint256 max) public {
        IERC721 erc721 = IERC721(collection);
        uint256 avail = erc721.balanceOf(msg.sender);
        uint256 currentToken;
        uint256 numberToTransfer = avail;
        if (max < avail) numberToTransfer = max;

        if (erc721.supportsInterface(_INTERFACE_ID_ERC721_ENUMERABLE)) {
            IERC721Enumerable enumerable = IERC721Enumerable(collection);
            for (uint256 i = numberToTransfer; i > 0; i--) {
                currentToken = enumerable.tokenOfOwnerByIndex(msg.sender, 0);
                erc721.safeTransferFrom(msg.sender, destination, currentToken);
            }
        } else {
            uint256 counter = startId;
            uint256 remaining = numberToTransfer;
            while (remaining > 0) {
                if (erc721.ownerOf(counter) == msg.sender) {
                    currentToken = counter;
                    erc721.safeTransferFrom(msg.sender, destination, counter);
                    --remaining;
                }
                counter++;
            }
        }

        emit BatchTransferred(numberToTransfer, currentToken);
    }   

    /**
     * Transfers the specified items in this collection to the destination address.
    */
    function batchTransferERC721(address collection, address destination, uint256[] calldata tokenIds) external {
        for (uint i = 0; i < tokenIds.length; i++) {
            IERC721 erc721 = IERC721(collection);
            erc721.safeTransferFrom(msg.sender, destination, tokenIds[i]);
        }
    }

    /**
     * Transfers the specified item in this collection to the destination address.
    */
    function transferERC721(address collection, address destination, uint256 tokenId) external {
        IERC721 erc721 = IERC721(collection);
        erc721.safeTransferFrom(msg.sender, destination, tokenId);
    }
}


// File: IERC1155.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @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;
}


// File: IERC165.sol



// SPDX-License-Identifier: None

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * [EIP](https://eips.ethereum.org/EIPS/eip-165).
 *
 * 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
     * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
     * 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);
}


// File: IERC721.sol



// SPDX-License-Identifier: None

pragma solidity ^0.8.0;

import "./IERC165.sol";

abstract contract IERC721 is IERC165 {
    function balanceOf(address owner) public virtual view returns (uint256 balance);
    function ownerOf(uint256 tokenId) public virtual view returns (address owner);
    function safeTransferFrom(address from, address to, uint256 tokenId) public virtual;
    function getApproved(uint256 tokenId) public virtual view returns (address operator);
    function isApprovedForAll(address owner, address operator) public virtual view returns (bool);
}


// File: IERC721Enumerable.sol



// SPDX-License-Identifier: None

pragma solidity ^0.8.0;

import "./IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
abstract contract IERC721Enumerable is IERC721 {
    function totalSupply() public virtual view returns (uint256);
    function tokenOfOwnerByIndex(address owner, uint256 index) public virtual view returns (uint256 tokenId);
    function tokenByIndex(uint256 index) public virtual view returns (uint256);
}