// Contract: BridgeQANX
// Target function: function bridgeSend(address beneficiary, uint256 amount, uint256 withdrawChainId) external returns (bytes32)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// MINIMAL ERC20 INTERFACE FOR QANX TRANSFERABILITY
interface TransferableERC20 {
    function transfer(address recipient, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}

contract Signed {

    // THE ADDRESSES PERMITTED TO SIGN WITHDRAWAL REQUESTS UP TO X AMOUNT
    mapping(address => uint256) internal signers;

    // SET NO LIMIT SIGNER ON DEPLOYMENT
    constructor() {
        signers[msg.sender] = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    }

    // METHOD TO SET WITHDRAWAL SIGNER ADDRESSES
    function setSigner(address signer, uint256 limit) external {
        require(signer != address(0) && signers[msg.sender] == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        signers[signer] = limit;
    }

    // METHOD TO VERIFY WITHDRAWAL SIGNATURE OF A GIVEN TXID & AMOUNT
    function verifySignature(bytes32 txid, bytes memory signature, uint256 amount) internal view returns (bool) {

        // SIGNATURE VARIABLES FOR ECRECOVER
        bytes32 r;
        bytes32 vs;

        // SPLIT SIGNATURE INTO r + vs
        assembly {
            r := mload(add(signature, 32))
            vs := mload(add(signature, 64))
        }

        // DETERMINE s AND v FROM vs
        bytes32 s = vs & 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
        uint8 v = 27 + uint8(uint256(vs) >> 255);

        // RECOVER & VERIFY SIGNER IDENTITY RELATED TO AMOUNT
        return amount < signers[ecrecover(txid, v, r, s)];
    }
}

contract BridgeQANX is Signed {

    // POINTS TO THE OFFICIAL QANX CONTRACT
    TransferableERC20 private _qanx = TransferableERC20(0xAAA7A10a8ee237ea61E8AC46C50A8Db8bCC1baaa);

    // STORES NONCES FOR CROSS-CHAIN TRANSFERS (msg.sender => depositChainId => withdrawChainId = nonce)
    mapping(address => mapping(uint256 => mapping(uint256 => uint256))) private _nonces;

    // FETCH NONCE OF SENDER BASED ON CHAIN IDS
    function getNonce(address sender, uint256 depositChainId, uint256 withdrawChainId) external view returns (uint256) {
        return _nonces[sender][depositChainId][withdrawChainId];
    }