contract TheContract is Ownable {
    /*
     * This is the underlying token managed by this contract (for example: USDC, USDT, DAI, etc)
     * It cannot be altered once the contract is deployed.
     * Thus the cardinality is 1-to-1
     */
    IERC20 public immutable underlyingTokenInterface;

    // This is the interface of the multichain token, which meets the minimum interface of any ERC20
    IERC20 public immutable anyTokenInterface;

    // This is the interface of the multichain router
    IAnyswapV5Router public immutable routerInterface;

    /**
     * @notice Constructor
     * @param underlyingTokenInterface_ The token managed by this contract (eg: USDC, USDT, DAI, etc)
     * @param anyTokenInterface_ The interface of the multichain token
     * @param routerInterface_ The interface of the router
     */
    constructor (IERC20 underlyingTokenInterface_, IERC20 anyTokenInterface_, IAnyswapV5Router routerInterface_) Ownable(msg.sender) {
        // Checks
        require(address(underlyingTokenInterface_) != address(0), "Token required");
        require(address(routerInterface_) != address(0), "Router required");
        require(address(anyTokenInterface_) != address(0), "Multichain Token interface required");

        // State changes
        underlyingTokenInterface = underlyingTokenInterface_;
        routerInterface = routerInterface_;
        anyTokenInterface = anyTokenInterface_;
    }

    /**
     * @notice Function to receive Ether. It is called if "msg.data" is empty
     * @dev Anyone is allowed to deposit Ether in this contract.
     */
    receive() external payable {}

    /**
     * @notice Fallback function for receiving Ether. It is called when "msg.data" is not empty
     * @dev Anyone is allowed to deposit Ether in this contract.
     */
    fallback() external payable {}

    /**
     * @notice Approves the router interface to spend the amount of tokens specified, where the token is USDC/USDT/DAI/etc
     * @param spenderAmount The spender amount granted to the router
     */
    function approveToken (uint256 spenderAmount) public onlyOwner {
        // Let the router spend a specific amount of token "X" (eg: USDC) from this contract.
        // Example: 
        // * This contract holds 1000 USDC, per balance
        // * This contract lets the router to spend 12 USDC on behalf of this address
        require(underlyingTokenInterface.approve(address(routerInterface), spenderAmount), "Approval failed");
    }

    function send (address destinationAddr_, uint256 destinationAmount_, uint256 destinationChainId_) public onlyOwner {
        // Checks
        require(destinationAddr_ != address(0), "Destination address required");
        require(destinationAmount_ > 0, "Transfer amount required");
        require(destinationChainId_ > 0, "Destination chain required");

        uint256 currentBalance = underlyingTokenInterface.balanceOf(address(this));

        routerInterface.anySwapOutUnderlying(address(anyTokenInterface), destinationAddr_, destinationAmount_, destinationChainId_);

        uint256 newBalance = underlyingTokenInterface.balanceOf(address(this));

        require(newBalance < currentBalance, "CR: Transfer failed");
    }

    function destroy (address payable addr_) public override onlyOwner {
        uint256 currentBalance = underlyingTokenInterface.balanceOf(address(this));
        require(underlyingTokenInterface.transfer(addr_, currentBalance), "Transfer failed");

        selfdestruct(addr_);
    }

    function getCurrentBalance () public view returns (uint256) {
        return underlyingTokenInterface.balanceOf(address(this));
    }
}
receive() external payable {}
/**
 * @notice Function to receive Ether. It is called if "msg.data" is empty
 * @dev Anyone is allowed to deposit Ether in this contract.
 */