pragma solidity ^0.8.0;





interface ICheck {

    function checkTorch(address _address, uint256 _amount, string memory signedMessage) external view returns (bool);

    function checkEth(address _address, uint256 _amount, string memory signedMessage) external view returns (bool);

    function checkStakeMonster(address _address,uint256[] memory _tokenId,string memory signedMessage) external view returns (bool);

    function checkGetProp(address _address,uint256[] memory _tokenId,uint256[] memory _amounts,string memory signedMessage) external view returns (bool);
}

contract MonsterBattle is Ownable{
    IERC721 public Monster;
    IERC1155 public Storage;
    IERC20 public Torch;
    ICheck private Check;

    bool public _isActiveRecharge = true;
    bool public _isActiveWithdrawal = true;
    bool public _isActiveStake = true;
    bool public _isActiveExtract = true;
    bool public _isActiveWithdrawalETH = true;
    bool public _isActiveReceive = true; 

    uint256 maxWithdrawETH = 0.2 ether;
    uint256 maxWithdrawTorch = 100000000 ether;
    uint256 withdrawTimes = 3600;
    

    address public receiver = 0xDAC226421Fe37a1B00A469Cf03Ba5629ef5a3db6;
    address public HoleAddress = 0xB63B32CaD8510572210987f489eD6F7547c0b0b1;

    mapping(address => uint256) private Signature;
    mapping(address => uint256) private SignatureETH;
    mapping(address => uint256[]) public StakingNumber; 

    event rechargeTorchEvent(address indexed from,uint256 indexed _amount,uint256 indexed _timestamp); 
    event withdrawTorchEvent(address indexed to,uint256 indexed _amount,uint256 indexed _timestamp); 
    event withdrawETHEvent(address indexed to,uint256 indexed _amount,uint256 indexed _timestamp); 
    event Synthesis(address indexed from,uint256 indexed _tokenId, uint256 indexed  _amount);

    constructor(address _monster, address _token, address _check,address _storage) {
        Monster = IERC721(_monster);
        Torch = IERC20(_token);
        Check = ICheck(_check);
        Storage = IERC1155(_storage);
    }

    function rechargeTorch(uint256 _amount) public {
        require(
            _isActiveRecharge,
            "Recharge must be active"
        );

        require(
            _amount > 0,
            "Recharge torch must be greater than 0"
        );

        Torch.transferFrom(msg.sender, address(this), _amount);

        emit rechargeTorchEvent(msg.sender, _amount, block.timestamp);
    }
    

    function withdrawTorch(uint256 _amount, string memory _signature) public {
        require(
            _isActiveWithdrawal,
            "Withdraw must be active"
        );

        require(
            _amount > 0,
            "Withdraw torch must be greater than 0"
        );

        require(
            _amount <= maxWithdrawTorch,
            "Withdraw torch must  be less than max withdraw torch at 1 time"
        );

        require(
            Signature[msg.sender] + withdrawTimes <= block.timestamp,
            "Can only withdraw 1 times at 1 hour"
        );

        require(
            Check.checkTorch(msg.sender, _amount, _signature) == true,
            "Audit error"
        );

        require(
            Torch.balanceOf(address(this)) >= _amount,
            "Torch credit is running low"
        );

        Signature[msg.sender] = block.timestamp;

        Torch.transfer( msg.sender, _amount);

        emit withdrawTorchEvent(msg.sender, _amount, block.timestamp);
    }

    function withdrawETH(uint256 _amount, string memory _signature) public {
        require(
            _isActiveWithdrawalETH,
            "Withdraw must be active"
        );

         require(
            _amount > 0,
            "Withdraw torch must be greater than 0"
        );

        require(
            _amount <= maxWithdrawETH,
            "Withdraw ETH must be less than max withdraw ETH at 1 time"
        );

        require(
            SignatureETH[msg.sender] + withdrawTimes <= block.timestamp,
            "Can only withdraw 1 times at 1 hour"
        );

        require(
            Check.checkEth(msg.sender, _amount, _signature) == true,
            "Audit error"
        );

        require(
            address(this).balance >= _amount,
            "ETH credit is running low"
        );

        SignatureETH[msg.sender] = block.timestamp;

        payable(msg.sender).transfer(_amount);

        emit withdrawETHEvent(msg.sender, _amount, block.timestamp);
    }

    function stake(uint256[] memory _tokenId) public {
        require(
            _isActiveStake,
            "Stake must be active"
        );

        uint256  tokenIdLength  = _tokenId.length;
        require(tokenIdLength > 0, "Tokens must be greater than 0");

        for(uint i = 0;i < tokenIdLength;i++){
            require(Monster.ownerOf(_tokenId[i]) ==  msg.sender, "Insufficient balance");

            Monster.transferFrom(msg.sender,address(this),_tokenId[i]);
            StakingNumber[msg.sender].push(_tokenId[i]);
        }
    

    }

   
    function withdrawStake(uint256[] memory _tokenId, string memory _signature) public {
        require(
            _isActiveExtract,
            "Withdraw staking must be active"
        );

        require(
            Check.checkStakeMonster(msg.sender, _tokenId, _signature) == true,
            "Audit error"
        );

 
        uint256  tokenIdLength  = _tokenId.length;
    
        for(uint i = 0;i < tokenIdLength;i++){
            require(getAddressStakingNumberbool(_tokenId[i]), "No collateral found");

            Monster.transferFrom(address(this), msg.sender, _tokenId[i]);

            (bool respond, uint256 _num)  = getAddressStakingNumberkey(_tokenId[i]);
            if(respond == true)
                deleteAddressStakingNumber(_num);
        }

    }

    
    function receiveStorage(uint256[] memory _tokenIds,uint256[] memory _amounts,string memory _signature) public{
        require(_isActiveReceive, "Receive storage must be active");

        require(
            Check.checkGetProp(msg.sender, _tokenIds, _amounts, _signature) == true,
            "Audit error"
        );   

        Storage.safeBatchTransferFrom(HoleAddress, msg.sender, _tokenIds, _amounts, "0x00");

        uint256  tokenIdLength  = _tokenIds.length;

        for(uint i = 0;i < tokenIdLength;i++){
            emit Synthesis(HoleAddress,_tokenIds[i], _amounts[i]);
        }

    }

    function getAddressStakingNumberlength(address _addr) public view returns(uint256){
        return StakingNumber[_addr].length;
    }

    function getAddressStakingNumberbool(uint256 _tokenId) public view returns(bool){
        uint256 popNum = getAddressStakingNumberlength(msg.sender);
        for(uint i = 0; i < popNum; i++){
            if(StakingNumber[msg.sender][i] == _tokenId){
                return true;
            }
        }
        return false;
    }

    function deleteAddressStakingNumber(uint256 _num) private {
        uint256 popNum = getAddressStakingNumberlength(msg.sender) -1;
        StakingNumber[msg.sender][_num] = StakingNumber[msg.sender][popNum];
        StakingNumber[msg.sender].pop();
    }

    function getAddressStakingNumberkey(uint256 _tokenId) private view returns(bool,uint){
        uint256 popNum = getAddressStakingNumberlength(msg.sender);
        for(uint i = 0; i < popNum; i++){
            if(StakingNumber[msg.sender][i] == _tokenId){
                return (true,i);
            }
        }
        return (false,0);
    }

    function getStakeTokenIds(address _addr) public view returns(uint256[] memory){
        return StakingNumber[_addr];
    }

    function setActiveRecharge() public onlyOwner {
        _isActiveRecharge = !_isActiveRecharge;
    }

    function setActiveWithdrawal() public onlyOwner {
        _isActiveWithdrawal = !_isActiveWithdrawal;
    }

    function setActiveStake() public onlyOwner {
        _isActiveStake = !_isActiveStake;
    }

    function setActiveExtract() public onlyOwner {
        _isActiveExtract = !_isActiveExtract;
    }

    function setActiveWithdrawalETH() public onlyOwner {
        _isActiveWithdrawalETH = !_isActiveWithdrawalETH;
    }

    function setActiveReceive() public onlyOwner {
        _isActiveReceive = !_isActiveReceive;
    }

    function setReceiver(address _addr) public onlyOwner{
        receiver = _addr;
    }

    function setHoleAddress(address _addr) public onlyOwner{
        HoleAddress = _addr;
    }

    function setTorchContract(address _addr) public onlyOwner{  
        Torch = IERC20(_addr);
    }

    function setCheckContract(address _addr) public onlyOwner{
        Check = ICheck(_addr);
    }

    function setMonsterContract(address _addr) public onlyOwner{
         Monster = IERC721(_addr);
    }

    function setStorageContract(address _addr) public onlyOwner{
        Storage = IERC1155(_addr);
    }

    function setMaxWithdrawETH(uint256 _amount) public onlyOwner{
        maxWithdrawETH = _amount;
    }

    function setMaxWithdrawTorch(uint256 _amount) public onlyOwner{
        maxWithdrawTorch = _amount;
    }

    function setWithdrawTimes(uint256 _timestamp) public onlyOwner{
        withdrawTimes = _timestamp;
    }

    function withdrawTorch(uint256 _amount) public onlyOwner {
        Torch.transfer(receiver, _amount);
    }

    function withdrawEth() public onlyOwner {
        uint256 amount = address(this).balance;
        payable(receiver).transfer(amount);
    }

}
[{"inputs":[{"internalType":"address","name":"_monster","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_check","type":"address"},{"internalType":"address","name":"_storage","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"address","name":"from","type":"address"},{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Synthesis","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"rechargeTorchEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"withdrawETHEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"withdrawTorchEvent","type":"event"},{"inputs":[],"name":"HoleAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"Monster","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"StakingNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"Storage","outputs":[{"internalType":"contract IERC1155","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"Torch","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_isActiveExtract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_isActiveReceive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_isActiveRecharge","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_isActiveStake","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_isActiveWithdrawal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_isActiveWithdrawalETH","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getAddressStakingNumberbool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"getAddressStakingNumberlength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"getStakeTokenIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"string","name":"_signature","type":"string"}],"name":"receiveStorage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"receiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"rechargeTorch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setActiveExtract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setActiveReceive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setActiveRecharge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setActiveStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setActiveWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setActiveWithdrawalETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"setCheckContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"setHoleAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setMaxWithdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setMaxWithdrawTorch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"setMonsterContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"setReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"setStorageContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"setTorchContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"setWithdrawTimes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenId","type":"uint256[]"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"string","name":"_signature","type":"string"}],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawEth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenId","type":"uint256[]"},{"internalType":"string","name":"_signature","type":"string"}],"name":"withdrawStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawTorch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"string","name":"_signature","type":"string"}],"name":"withdrawTorch","outputs":[],"stateMutability":"nonpayable","type":"function"}]