// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

// External Dependencies
import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import {ConfirmedOwnerWithProposal} from "@chainlink/contracts-ccip/src/v0.8/shared/access/ConfirmedOwnerWithProposal.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import {IERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol";

/**
 * AMO - Token Bridge Contract
 * Core Bridge contract built on top of CCIP Protocol to swap AMO tokens across chains.
 */

/// @title AMO Bridge Contract for transferring/receiving AMO tokens across chains.
contract AmoTokenBridge is CCIPReceiver, ConfirmedOwnerWithProposal {
    // Custom errors to provide more descriptive revert messages.
    error NotEnoughApproval(address target, uint256 amountRequired); // Used to make sure contract has enough approval to withdraw token deposit.
    error NotEnoughBalance(uint256 expected, uint256 available); // Used to make sure contract has enough balance to cover the fees or withdraw.
    error NothingToWithdraw(); // Used when trying to withdraw Ether but there's nothing to withdraw.
    error FailedToWithdraw(address owner, address target, uint256 value); // Used when the transfer of Ether or token fails.
    error DestinationChainNotAllowed(uint64 destinationChainSelector); // Used when the destination chain has not been allowlisted by the contract owner.
    error SourceChainNotAllowed(uint64 sourceChainSelector); // Used when the source chain has not been allowlisted by the contract owner.
    error SenderNotAllowed(address sender); // Used when the sender has not been allowlisted by the contract owner.

    // Event emitted when a message is sent to another chain.
    event MessageSent(
        bytes32 indexed messageId, // The unique ID of the CCIP message.
        uint64 indexed destinationChainSelector, // The chain selector of the destination chain.
        address receiver, // The address of the receiver on the destination chain.
        address tokenRecipient, // The address of the token recipient on the destination chain.
        address token, // The token address that was transferred.
        uint256 tokenAmount, // The token amount that was transferred.
        address feeToken, // the token address used to pay CCIP fees.
        uint256 fees // The fees paid for sending the message.
    );

    // Event emitted when a message is received from another chain.
    event MessageReceived(
        bytes32 indexed messageId, // The unique ID of the CCIP message.
        uint64 indexed sourceChainSelector, // The chain selector of the source chain.
        address sender, // The address of the sender from the source chain.
        address tokenRecipient, // The address of the token recipient on the destination chain.
        address token, // The token address that was transferred.
        uint256 tokenAmount // The token amount that was transferred.
    );

    bytes32 private s_lastReceivedMessageId; // Store the last received messageId.
    address private s_lastReceivedSender; // Store the last sender address.
    address private s_lastReceivedTokenRecipient; // Store the last received token recipient Address.
    address private s_lastReceivedTokenAddress; // Store the last received token address.
    uint256 private s_lastReceivedTokenAmount; // Store the last received amount.

    IRouterClient private ccipRouter; // Router client instance to interact with cross-chain router.
    uint256 public maxGasLimit = 200000; // Maximum amount of gas CCIP can consume to execute ccipReceive() on dest chain.

    // Mapping to keep track of allowlisted destination chains.
    mapping(uint64 => bool) public allowlistedDestinationChains;

    // Mapping to keep track of allowlisted source chains.
    mapping(uint64 => bool) public allowlistedSourceChains;

    // Mapping to keep track of allowlisted senders.
    mapping(address => bool) public allowlistedSenders;

    /// @notice Constructor initializes the contract with the router address.
    /// @param _router The address of the router contract.
    constructor(address _router) CCIPReceiver(_router) ConfirmedOwnerWithProposal(msg.sender, address(0)){
        require(_router != address(0), "Invalid router address");
        ccipRouter = IRouterClient(_router);
    }

    /// @dev Modifier that checks if the chain with the given destinationChainSelector is allowlisted.
    /// @param _destinationChainSelector The selector of the destination chain.
    modifier onlyAllowlistedDestinationChain(uint64 _destinationChainSelector) {
        if (!allowlistedDestinationChains[_destinationChainSelector])
            revert DestinationChainNotAllowed(_destinationChainSelector);
        _;
    }

    /// @dev Modifier that checks if the chain with the given sourceChainSelector is allowlisted and if the sender is allowlisted.
    /// @param _sourceChainSelector The selector of the destination chain.
    /// @param _sender The address of the sender.
    modifier onlyAllowlisted(uint64 _sourceChainSelector, address _sender) {
        if (!allowlistedSourceChains[_sourceChainSelector])
            revert SourceChainNotAllowed(_sourceChainSelector);
        if (!allowlistedSenders[_sender]) revert SenderNotAllowed(_sender);
        _;
    }

    /// @dev Updates the allowlist status of a destination chain for transactions.
    /// @notice This function can only be called by the owner.
    /// @param _destinationChainSelector The selector of the destination chain to be updated.
    /// @param allowed The allowlist status to be set for the destination chain.
    function allowlistDestinationChain( uint64 _destinationChainSelector, bool allowed) external onlyOwner {
        allowlistedDestinationChains[_destinationChainSelector] = allowed;
    }

    /// @dev Updates the allowlist status of a source chain
    /// @notice This function can only be called by the owner.
    /// @param _sourceChainSelector The selector of the source chain to be updated.
    /// @param allowed The allowlist status to be set for the source chain.
    function allowlistSourceChain(uint64 _sourceChainSelector, bool allowed) external onlyOwner {
        allowlistedSourceChains[_sourceChainSelector] = allowed;
    }

    /// @dev Updates the allowlist status of a sender for transactions.
    /// @notice This function can only be called by the owner.
    /// @param _sender The address of the sender to be updated.
    /// @param allowed The allowlist status to be set for the sender.
    function allowlistSender(address _sender, bool allowed) external onlyOwner {
        allowlistedSenders[_sender] = allowed;
    }


    /// @notice Sends data and transfer tokens to receiver on the destination chain.
    /// @notice Get estimate for fees in native gas.
    /// @dev Assumes your contract has sufficient native gas like ETH on Ethereum or MATIC on Polygon.
    /// @param _destinationChainSelector The identifier (aka selector) for the destination blockchain.
    /// @param _receiver The address of the recipient on the destination blockchain.
    /// @param _tokenRecipient The address of the token recipient on the destination chain.
    /// @param _token token address.
    /// @param _amount token amount.
    /// @return fees The fees required for sending this message.
    function estimateFees(
        uint64 _destinationChainSelector,
        address _receiver,
        address _tokenRecipient,
        address _token,
        uint256 _amount
    ) public view returns (uint256 fees) {
        // Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message
        // address(0) means fees are paid in native gas
        Client.EVM2AnyMessage memory evm2AnyMessage = _buildCCIPMessage(
            _receiver,
            _tokenRecipient,
            _token,
            _amount,
            address(0)
        );

        // Get and return the fees required to send the CCIP message
        return ccipRouter.getFee(
            _destinationChainSelector,
            evm2AnyMessage
        );
    }


    /// @notice Fetch the token allowance approved by the caller address 
    /// @dev This function get the allowance against msg.sender and the contract address
    /// @param _token The token address to validate the approval provided.
    function getAllowance(address _token) public view returns (uint256 allowedAmount) {
        return IERC20(_token).allowance(msg.sender, address(this));
    }


    /// @notice Sends data and transfer tokens to receiver on the destination chain.
    /// @notice Pay for fees in native gas.
    /// @dev Assumes your contract has sufficient native gas like ETH on Ethereum or MATIC on Polygon.
    /// @param _destinationChainSelector The identifier (aka selector) for the destination blockchain.
    /// @param _receiver The address of the recipient on the destination blockchain.
    /// @param _tokenRecipient The address of the token recipient on the destination chain.
    /// @param _token token address.
    /// @param _amount token amount.
    /// @return messageId The ID of the CCIP message that was sent.
    function bridgeToken(
        uint64 _destinationChainSelector,
        address _receiver,
        address _tokenRecipient,
        address _token,
        uint256 _amount
    )
        external
        payable
        onlyAllowlistedDestinationChain(_destinationChainSelector)
        returns (bytes32 messageId)
    {
        // Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message
        // address(0) means fees are paid in native gas
        Client.EVM2AnyMessage memory evm2AnyMessage = _buildCCIPMessage(
            _receiver,
            _tokenRecipient,
            _token,
            _amount,
            address(0)
        );

        // Get the fee required and validate the msg.value provided to send the CCIP message
        uint256 fees = ccipRouter.getFee(_destinationChainSelector, evm2AnyMessage);
        if (fees > msg.value) revert NotEnoughBalance(fees, msg.value); 

        // Validate if the contract is approved to withdraw tokens on the user's behalf
        uint256 tokenAllowance = IERC20(_token).allowance(msg.sender, address(this));
        if (_amount > tokenAllowance) revert NotEnoughApproval(address(this), _amount - tokenAllowance);

        // Transfer the user's native balance for the gas fees to the contract
        (bool sentEth, ) = address(this).call{value: msg.value}("");
        if (!sentEth) revert FailedToWithdraw(msg.sender, address(this), msg.value);

        // Transfer the user's bridge token amount to the contract
        bool sentToken = IERC20(_token).transferFrom(msg.sender, address(this), _amount);
        if (!sentToken) revert FailedToWithdraw(msg.sender, address(this), _amount);

        // Approve the CCIP router to transfer tokens deposited by the user
        IERC20(_token).approve(address(ccipRouter), _amount);

        // Send the message through the router and store the returned message ID
        messageId = ccipRouter.ccipSend{value: fees}(_destinationChainSelector, evm2AnyMessage);

        // Emit an event with message details
        emit MessageSent( messageId, _destinationChainSelector, _receiver, _tokenRecipient, _token, _amount, address(0), fees);

        // Return the message ID
        return messageId;
    }

    
    /// @notice Allows the contract owner to change the CCIP router address from within the contract.
    /// @dev This function allows the owner to update the router in case the Chainlink router changes.
    /// It can only be called by the owner of the contract.
    /// @param _router The new address of the CCIP router to be set.
    function changeCCIPRouter(address _router) external onlyOwner {
        // Update the CCIP router client instance
        ccipRouter = IRouterClient(_router);
    }


    /// @notice Allows the contract owner to change the maximum gas limit set for CCIP messages.
    /// @dev This function updates the gas limit used when the bridge is triggered.
    /// It can only be called by the owner of the contract.
    /// @param _newLimit The new gas limit to be set.
    function changeGasLimit(uint256 _newLimit) external onlyOwner {
        maxGasLimit = _newLimit;
    }

    /// @notice Allows the contract owner to withdraw a specified amount of Ether to a designated beneficiary address.
    /// @dev This function reverts if there are no funds to withdraw or if the transfer fails.
    /// It can only be called by the owner of the contract.
    /// @param _beneficiary The address to which the Ether should be sent.
    /// @param _requestAmount The amount of Ether to withdraw.
    function withdraw(address _beneficiary, uint256 _requestAmount) external onlyOwner{
        // Retrieve the native balance of this contract
        uint256 availableBalance = address(this).balance;

        // Revert if there is nothing to withdraw
        if (availableBalance == 0) revert NothingToWithdraw();

        // Revert if the requested amount is not available to withdraw
        if (_requestAmount > availableBalance)
            revert NotEnoughBalance(availableBalance, _requestAmount);

        // Attempt to send the funds, capturing the success status and discarding any return data
        (bool sent, ) = _beneficiary.call{value: _requestAmount}("");

        // Revert if the send failed, with information about the attempted transfer
        if (!sent)
            revert FailedToWithdraw(
                msg.sender,
                _beneficiary,
                _requestAmount
            );
    }


    /// @notice Allows the contract owner to withdraw a specified amount of tokens to a designated beneficiary address.
    /// @dev This function reverts if there are no funds to withdraw or if the token transfer fails.
    /// It can only be called by the owner of the contract.
    /// @param _beneficiary The address to which the tokens should be sent.
    /// @param _token The address of the token to withdraw.
    /// @param _requestAmount The amount of tokens to withdraw.
    function withdraw(address _beneficiary, address _token,  uint256 _requestAmount) external onlyOwner{
        // Retrieve the token balance on this contract
        uint256 availableBalance = IERC20(_token).balanceOf(address(this));

        // Revert if there is nothing to withdraw
        if (availableBalance == 0) revert NothingToWithdraw();

        // Revert if the requested amount is not available to withdraw
        if (_requestAmount > availableBalance)
            revert NotEnoughBalance(availableBalance, _requestAmount);

        // Transfer tokens to the beneficiary
        IERC20(_token).transfer(_beneficiary, _requestAmount);
    }


    /**
     * @notice Returns the details of the last CCIP received message.
     * @dev This function retrieves the ID, text, token address, and token amount of the last received CCIP message.
     * @return messageId The ID of the last received CCIP message.
     * @return sender The address of the sender triggered in the last received CCIP message.
     * @return tokenRecipient The token recipient of the token in the last received CCIP message.
     * @return tokenAddress The address of the token in the last CCIP received message.
     * @return tokenAmount The amount of the token in the last CCIP received message.
     */
    function getLastReceivedMessageDetails()
        public
        view
        returns (
            bytes32 messageId,
            address sender,
            address tokenRecipient,
            address tokenAddress,
            uint256 tokenAmount
        )
    {
        return (
            s_lastReceivedMessageId,
            s_lastReceivedSender,
            s_lastReceivedTokenRecipient,
            s_lastReceivedTokenAddress,
            s_lastReceivedTokenAmount
        );
    }


    /// handle a received message
    function _ccipReceive(Client.Any2EVMMessage memory any2EvmMessage)
        internal
        override
        onlyAllowlisted(
            any2EvmMessage.sourceChainSelector,
            abi.decode(any2EvmMessage.sender, (address))
        ) // Make sure source chain and sender are allowlisted
    {
        s_lastReceivedMessageId = any2EvmMessage.messageId; // fetch the messageId
        s_lastReceivedSender = abi.decode(any2EvmMessage.sender, (address)); // abi-decoding of the sender address
        s_lastReceivedTokenRecipient = abi.decode(any2EvmMessage.data,(address)); // abi-decoding of the sent address
        // Expect one token to be transferred at once, but you can transfer several tokens.
        s_lastReceivedTokenAddress = any2EvmMessage.destTokenAmounts[0].token;
        s_lastReceivedTokenAmount = any2EvmMessage.destTokenAmounts[0].amount;

        // Perform ERC-20 token transfer to the token recipient
        IERC20(s_lastReceivedTokenAddress).transfer(
            s_lastReceivedTokenRecipient,
            s_lastReceivedTokenAmount
        );

        emit MessageReceived(
            any2EvmMessage.messageId,
            any2EvmMessage.sourceChainSelector, // fetch the source chain identifier (aka selector)
            s_lastReceivedSender,
            s_lastReceivedTokenRecipient,
            s_lastReceivedTokenAddress,
            s_lastReceivedTokenAmount
        );
    }


    /// @notice Construct a CCIP message.
    /// @dev This function will create an EVM2AnyMessage struct with all the necessary information for programmable tokens transfer.
    /// @param _receiver The address of the receiver.
    /// @param _tokenRecipient The address of the token recipient to receive the token.
    /// @param _token The token to be transferred.
    /// @param _amount The amount of the token to be transferred.
    /// @param _feeTokenAddress The address of the token used for fees. Set address(0) for native gas.
    /// @return Client.EVM2AnyMessage Returns an EVM2AnyMessage struct which contains information for sending a CCIP message.
    function _buildCCIPMessage(
        address _receiver,
        address _tokenRecipient,
        address _token,
        uint256 _amount,
        address _feeTokenAddress
    ) internal view returns (Client.EVM2AnyMessage memory) {
        // Set the token amounts
        Client.EVMTokenAmount[]
            memory tokenAmounts = new Client.EVMTokenAmount[](1);
        tokenAmounts[0] = Client.EVMTokenAmount({
            token: _token,
            amount: _amount
        });
        // Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message
        return
            Client.EVM2AnyMessage({
                receiver: abi.encode(_receiver), // ABI-encoded receiver address
                data: abi.encode(_tokenRecipient), // ABI-encoded token recipient address
                tokenAmounts: tokenAmounts, // The amount and type of token being transferred
                extraArgs: Client._argsToBytes(
                    // Additional arguments, setting gas limit and non-strict sequencing mode
                    Client.EVMExtraArgsV1({gasLimit: maxGasLimit})
                ),
                // Set the feeToken to a feeTokenAddress, indicating specific asset will be used for fees
                feeToken: _feeTokenAddress
            });
    }


    /// @notice Fallback function to allow the contract to receive Ether.
    /// @dev This function has no function body, making it a default function for receiving Ether.
    /// It is automatically called when Ether is sent to the contract without any data.
    receive() external payable {}

}
[{"inputs":[{"internalType":"address","name":"_router","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint64","name":"destinationChainSelector","type":"uint64"}],"name":"DestinationChainNotAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"FailedToWithdraw","type":"error"},{"inputs":[{"internalType":"address","name":"router","type":"address"}],"name":"InvalidRouter","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amountRequired","type":"uint256"}],"name":"NotEnoughApproval","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"NotEnoughBalance","type":"error"},{"inputs":[],"name":"NothingToWithdraw","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"SenderNotAllowed","type":"error"},{"inputs":[{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"}],"name":"SourceChainNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"messageId","type":"bytes32"},{"indexed":true,"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"tokenRecipient","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"MessageReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"messageId","type":"bytes32"},{"indexed":true,"internalType":"uint64","name":"destinationChainSelector","type":"uint64"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"address","name":"tokenRecipient","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"feeToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fees","type":"uint256"}],"name":"MessageSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_destinationChainSelector","type":"uint64"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"allowlistDestinationChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"allowlistSender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_sourceChainSelector","type":"uint64"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"allowlistSourceChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"allowlistedDestinationChains","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allowlistedSenders","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"allowlistedSourceChains","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"_destinationChainSelector","type":"uint64"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_tokenRecipient","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"bridgeToken","outputs":[{"internalType":"bytes32","name":"messageId","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"messageId","type":"bytes32"},{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"internalType":"bytes","name":"sender","type":"bytes"},{"internalType":"bytes","name":"data","type":"bytes"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Client.EVMTokenAmount[]","name":"destTokenAmounts","type":"tuple[]"}],"internalType":"struct Client.Any2EVMMessage","name":"message","type":"tuple"}],"name":"ccipReceive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_router","type":"address"}],"name":"changeCCIPRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newLimit","type":"uint256"}],"name":"changeGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_destinationChainSelector","type":"uint64"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_tokenRecipient","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"estimateFees","outputs":[{"internalType":"uint256","name":"fees","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getAllowance","outputs":[{"internalType":"uint256","name":"allowedAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastReceivedMessageDetails","outputs":[{"internalType":"bytes32","name":"messageId","type":"bytes32"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"tokenRecipient","type":"address"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxGasLimit","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":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_requestAmount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"uint256","name":"_requestAmount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]