pragma solidity 0.8.17;








contract UnchartedMarketplace is ERC1155Holder, Ownable {

    // For airdrop

    IERC20 public usdcAddress;



    address payable public walletToSend; // The wallet where ETH from purchases should be sent



    IERC1155 public tokenAddress;

    uint256 public tokenId = 0;

    uint256 public tokenPrice = 19 * 1e16; // in the native currency (ETH) - wei

    // allowable smart contract NFT limit (for pre-sale and sale limitation)

    uint256 public allowableSCLimit = 0;

    uint256 public limitPerWallet = 0;



    event BuyEvent(address indexed buyer, IERC1155 indexed tokenAddress, uint256 indexed tokenId, uint256 amount);

    event GiveAwayEvent(IERC1155 indexed tokenAddress, address indexed recipient, uint256 indexed tokenId, uint256 amount);

    event PriceChangedEvent(uint256 indexed price);

    event UsdcAddressChangedEvent(address indexed usdcAddress);

    event WalletToSendAddressChangedEvent(address indexed newAddress);

    event AllowableSCLimitChangedEvent(uint256 indexed amount);

    event UnchartedTokenAddressChangedEvent(address indexed _address);

    event TokenIdChangedEvent(uint256 indexed _tokenId);

    event LimitPerWalletChangedEvent(uint256 indexed amount);



    constructor(

        address _usdcAddress,

        address payable _walletToSend,

        address _tokenAddress

    ) {

        walletToSend = _walletToSend;

        usdcAddress = IERC20(_usdcAddress);

        tokenAddress = IERC1155(_tokenAddress);

    }



    function setTokenId(uint256 _tokenId) public onlyOwner {

        tokenId = _tokenId;



        emit TokenIdChangedEvent(_tokenId);

    }



    function setTokenPrice(uint256 price) public onlyOwner {

        tokenPrice = price;



        emit PriceChangedEvent(price);

    }



    function setUnchartedTokenAddress(address _address) public onlyOwner {

        tokenAddress = IERC1155(_address);



        emit UnchartedTokenAddressChangedEvent(_address);

    }



    function setAllowableSCLimit(uint256 amount) public onlyOwner {

        allowableSCLimit = amount;



        emit AllowableSCLimitChangedEvent(amount);

    }



    function startNewSales(

        address _tokenAddress,

        uint256 _tokenId,

        uint256 price,

        uint256 _allowableSCLimit

    ) external onlyOwner {

        setUnchartedTokenAddress(_tokenAddress);

        setTokenId(_tokenId);

        setTokenPrice(price);

        setAllowableSCLimit(_allowableSCLimit);

    }



    function setUsdcAddress(address _usdcAddress) external onlyOwner {

        require(_usdcAddress != address(0x0), "Invalid address");

        usdcAddress = IERC20(_usdcAddress);



        emit UsdcAddressChangedEvent(_usdcAddress);

    }



    function setWalletToSend(address payable newAddress) external onlyOwner {

        require(newAddress != address(0x0), "Invalid address");

        walletToSend = newAddress;



        emit WalletToSendAddressChangedEvent(newAddress);

    }



    function setLimitPerWallet(uint256 amount) external onlyOwner {

        limitPerWallet = amount;



        emit LimitPerWalletChangedEvent(amount);

    }



    function buy(uint256 amount) external payable {

        require(tokenPrice > 0, "Not for sale yet");

        require(amount * tokenPrice == msg.value, "ETH amount mismatch");

        require(tokenAddress.balanceOf(address(this), tokenId) >= amount, "There is no such amount of tokens");

        require(

            tokenAddress.balanceOf(address(this), tokenId) - amount >= allowableSCLimit,

            string.concat("Limit reached")

        );

        if (limitPerWallet > 0) {

            require(

                tokenAddress.balanceOf(msg.sender, tokenId) + amount <= limitPerWallet,

                "Limit per wallet reached"

            );

        }



        walletToSend.transfer(msg.value);

        tokenAddress.safeTransferFrom(address(this), msg.sender, tokenId, amount, "0x0");



        emit BuyEvent(msg.sender, tokenAddress, tokenId, amount);

    }



    function giveAway(address recipient, uint256 amount) external onlyOwner {

        tokenAddress.safeTransferFrom(address(this), recipient, tokenId, amount, "GiveAway");



        emit GiveAwayEvent(tokenAddress, recipient, tokenId, amount);

    }



    function giveAwayBatch(address[] memory recipients, uint256[] memory amount) external onlyOwner {

        require(recipients.length == amount.length, "Recipient and amount arrays mismatch");

        uint sum = 0;

        for (uint i = 0; i < recipients.length; i++) {

            require(recipients[i] != address(0), "Recipient should not have 0x0 address");

            sum += amount[i];

        }

        require(tokenAddress.balanceOf(msg.sender, tokenId) >= sum, "Insufficient amount of tokens");



        for (uint i = 0; i < recipients.length; i++) {

            tokenAddress.safeTransferFrom(address(this), recipients[i], tokenId, amount[i], "GiveAway");

            emit GiveAwayEvent(tokenAddress, recipients[i], tokenId, amount[i]);

        }

    }



    function airdrop(address[] memory recipients, uint256[] memory amount) external {

        require(recipients.length == amount.length, "Recipient and amount arrays mismatch");

        uint sum = 0;

        for (uint i = 0; i < recipients.length; i++) {

            require(recipients[i] != address(0), "Recipient should not have 0x0 address");

            sum += amount[i];

        }

        require(usdcAddress.allowance(msg.sender, address(this)) >= sum, "Insufficient USDC allowance");

        require(usdcAddress.balanceOf(msg.sender) >= sum, "Insufficient USDC");



        for (uint i = 0; i < recipients.length; i++) {

            require(

                usdcAddress.transferFrom(msg.sender, recipients[i], amount[i]) == true,

                "Unable to transfer"

            );

        }

    }



    // If a user sends other tokens to this smart contract, the owner can transfer it to himself.

    function withdrawToken(address _tokenContract, address _recipient, uint256 _amount) external onlyOwner {

        IERC20 tokenContract = IERC20(_tokenContract);



        // transfer the token from address of this contract

        // to address of the user (executing the withdrawToken() function)

        tokenContract.transfer(_recipient, _amount);

    }

}
[{"inputs":[{"internalType":"address","name":"_usdcAddress","type":"address"},{"internalType":"address payable","name":"_walletToSend","type":"address"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AllowableSCLimitChangedEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"contract IERC1155","name":"tokenAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BuyEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC1155","name":"tokenAddress","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"GiveAwayEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LimitPerWalletChangedEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"price","type":"uint256"}],"name":"PriceChangedEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"TokenIdChangedEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_address","type":"address"}],"name":"UnchartedTokenAddressChangedEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"usdcAddress","type":"address"}],"name":"UsdcAddressChangedEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAddress","type":"address"}],"name":"WalletToSendAddressChangedEvent","type":"event"},{"inputs":[{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"amount","type":"uint256[]"}],"name":"airdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"allowableSCLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"giveAway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"amount","type":"uint256[]"}],"name":"giveAwayBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"limitPerWallet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setAllowableSCLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setLimitPerWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"setTokenId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"name":"setTokenPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"setUnchartedTokenAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_usdcAddress","type":"address"}],"name":"setUsdcAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newAddress","type":"address"}],"name":"setWalletToSend","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"_allowableSCLimit","type":"uint256"}],"name":"startNewSales","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenAddress","outputs":[{"internalType":"contract IERC1155","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usdcAddress","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"walletToSend","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenContract","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"}]