creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin/contracts/access/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {Context} from \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is set to the address provided by the deployer. This can\n * later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    /**\n     * @dev The caller account is not authorized to perform an operation.\n     */\n    error OwnableUnauthorizedAccount(address account);\n\n    /**\n     * @dev The owner is not a valid owner account. (eg. `address(0)`)\n     */\n    error OwnableInvalidOwner(address owner);\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n     */\n    constructor(address initialOwner) {\n        if (initialOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(initialOwner);\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        if (owner() != _msgSender()) {\n            revert OwnableUnauthorizedAccount(_msgSender());\n        }\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        if (newOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/Pausable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)\n\npragma solidity ^0.8.20;\n\nimport {Context} from \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n    bool private _paused;\n\n    /**\n     * @dev Emitted when the pause is triggered by `account`.\n     */\n    event Paused(address account);\n\n    /**\n     * @dev Emitted when the pause is lifted by `account`.\n     */\n    event Unpaused(address account);\n\n    /**\n     * @dev The operation failed because the contract is paused.\n     */\n    error EnforcedPause();\n\n    /**\n     * @dev The operation failed because the contract is not paused.\n     */\n    error ExpectedPause();\n\n    /**\n     * @dev Initializes the contract in unpaused state.\n     */\n    constructor() {\n        _paused = false;\n    }\n\n    /**\n     * @dev Modifier to make a function callable only when the contract is not paused.\n     *\n     * Requirements:\n     *\n     * - The contract must not be paused.\n     */\n    modifier whenNotPaused() {\n        _requireNotPaused();\n        _;\n    }\n\n    /**\n     * @dev Modifier to make a function callable only when the contract is paused.\n     *\n     * Requirements:\n     *\n     * - The contract must be paused.\n     */\n    modifier whenPaused() {\n        _requirePaused();\n        _;\n    }\n\n    /**\n     * @dev Returns true if the contract is paused, and false otherwise.\n     */\n    function paused() public view virtual returns (bool) {\n        return _paused;\n    }\n\n    /**\n     * @dev Throws if the contract is paused.\n     */\n    function _requireNotPaused() internal view virtual {\n        if (paused()) {\n            revert EnforcedPause();\n        }\n    }\n\n    /**\n     * @dev Throws if the contract is not paused.\n     */\n    function _requirePaused() internal view virtual {\n        if (!paused()) {\n            revert ExpectedPause();\n        }\n    }\n\n    /**\n     * @dev Triggers stopped state.\n     *\n     * Requirements:\n     *\n     * - The contract must not be paused.\n     */\n    function _pause() internal virtual whenNotPaused {\n        _paused = true;\n        emit Paused(_msgSender());\n    }\n\n    /**\n     * @dev Returns to normal state.\n     *\n     * Requirements:\n     *\n     * - The contract must be paused.\n     */\n    function _unpause() internal virtual whenPaused {\n        _paused = false;\n        emit Unpaused(_msgSender());\n    }\n}\n"
      },
      "contracts/BMCFaucetBridge.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\nimport {Pausable} from \"@openzeppelin/contracts/utils/Pausable.sol\";\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport {IBMCFaucetBridge} from \"./interfaces/IBMCFaucetBridge.sol\";\n\n/// @title Ethereum faucet bridge to Boomchain (ETH, WBTC, USDT, USDC --> BMC)\n/// @author BoomDAO\n/// @notice Deposit ETH or listed tokens and retrieve BMC on Boomchain as of the current exchange rate on Boomswap\n/// @custom:security-contact bsirt@boomdao.org\ncontract BMCFaucetBridge is IBMCFaucetBridge, Pausable, Ownable {\n    // address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;\n    // address private constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;\n    // address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; XXX: not supported yet --> require SafeERC20\n\n    uint256 private _nonce;\n\n    address public signer;\n\n    mapping(address => bool) public supportedTokens;\n\n    event BuyBMC(uint256 indexed nonce, address indexed from, address indexed tokenIn, uint256 amount);\n\n    error RevertTransfer();\n    error InvalidNonce();\n    error InvalidToken();\n    error InvalidAmount();\n    error InvalidSignature();\n    error InsufficientBalance();\n    error InsufficientAllowance();\n\n    /// @param _owner can pause the contract and withdraw funds\n    /// @param _signer is the frontend address that will sign the buyBMCWithETH transaction\n    constructor(address _owner, address _signer) Ownable(_owner) {\n        signer = _signer;\n\n        // BTC\n        supportedTokens[0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599] = true;\n        // USDC\n        supportedTokens[0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48] = true;\n        // USDT\n        // supportedTokens[0xdAC17F958D2ee523a2206206994597C13D831ec7] = true;\n    }\n\n    /**\n     * @dev See {IBMCFaucetBridge-buyBMCWithETH}.\n     */\n    function buyBMCWithETH(uint256 nonce, uint256 amount, bytes calldata signature) external payable whenNotPaused {\n        if (nonce != _nonce) revert InvalidNonce();\n\n        bytes32 message = _prefixed(keccak256(abi.encodePacked(nonce, msg.sender, amount)));\n        if (_recoverSigner(message, signature) != signer) revert InvalidSignature();\n\n        if (msg.value != amount) revert InvalidAmount();\n\n        _nonce = nonce + 1;\n\n        emit BuyBMC(nonce, msg.sender, address(0), amount);\n    }\n\n    /**\n     * @dev See {IBMCFaucetBridge-buyBMCWithToken}.\n     */\n    function buyBMCWithToken(uint256 nonce, address token, uint256 amount, bytes calldata signature)\n        external\n        whenNotPaused\n    {\n        if (nonce != _nonce) revert InvalidNonce();\n\n        bytes32 message = _prefixed(keccak256(abi.encodePacked(nonce, msg.sender, token, amount)));\n        if (_recoverSigner(message, signature) != signer) revert InvalidSignature();\n\n        if (!supportedTokens[token]) revert InvalidToken();\n        if (IERC20(token).allowance(msg.sender, address(this)) < amount) revert InsufficientAllowance();\n\n        _nonce = nonce + 1;\n\n        bool success = IERC20(token).transferFrom(msg.sender, address(this), amount);\n        if (!success) revert RevertTransfer();\n\n        emit BuyBMC(nonce, msg.sender, token, amount);\n    }\n\n    // ******************* VIEW FUNCTIONS *******************\n\n    function getNextNonce() external view returns (uint256) {\n        return _nonce;\n    }\n\n    function erc20Balance(address token) external view returns (uint256) {\n        return _erc20Balance(token);\n    }\n\n    // ======================================================\n    //                    ADMIN FUNCTIONS\n    // ======================================================\n\n    /**\n     * @dev See {IBMCFaucetBridge-withdraw}.\n     */\n    function withdraw(address to, uint256 amount) external onlyOwner {\n        if (amount > address(this).balance) revert InsufficientBalance();\n\n        (bool success,) = payable(to).call{value: amount}(\"\");\n        if (!success) revert RevertTransfer();\n    }\n\n    /**\n     * @dev See {IFaucetSwapper-withdrawERC20}.\n     */\n    function withdrawERC20(address token, address to, uint256 amount) external onlyOwner {\n        if (_erc20Balance(token) < amount) revert InsufficientBalance();\n        bool success = IERC20(token).transfer(to, amount);\n        if (!success) revert RevertTransfer();\n    }\n\n    /**\n     * @dev See {IBMCFaucetBridge-newSigner}.\n     */\n    function newSigner(address _signer) external onlyOwner {\n        signer = _signer;\n    }\n\n    // ***************** PAUSABLE FUNCTIONS *****************\n\n    function pause() external onlyOwner {\n        _pause();\n    }\n\n    function unpause() external onlyOwner {\n        _unpause();\n    }\n\n    // ======================================================\n    //                   INTERNAL FUNCTIONS\n    // ======================================================\n\n    function _erc20Balance(address token) private view returns (uint256) {\n        return IERC20(token).balanceOf(address(this));\n    }\n\n    // ======================================================\n    //                 SIGNATURE VERIFICATION\n    // ======================================================\n\n    function _prefixed(bytes32 hash) private pure returns (bytes32) {\n        return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n    }\n\n    function _recoverSigner(bytes32 message, bytes memory sig) private pure returns (address) {\n        uint8 v;\n        bytes32 r;\n        bytes32 s;\n        (v, r, s) = _splitSignature(sig);\n        return ecrecover(message, v, r, s);\n    }\n\n    function _splitSignature(bytes memory sig) private pure returns (uint8, bytes32, bytes32) {\n        require(sig.length == 65);\n        bytes32 r;\n        bytes32 s;\n        uint8 v;\n        assembly {\n            // first 32 bytes, after the length prefix\n            r := mload(add(sig, 32))\n            // second 32 bytes\n            s := mload(add(sig, 64))\n            // final byte (first byte of the next 32 bytes)\n            v := byte(0, mload(add(sig, 96)))\n        }\n        return (v, r, s);\n    }\n}\n"
      },
      "contracts/interfaces/IBMCFaucetBridge.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\ninterface IBMCFaucetBridge {\n    /**\n     * @notice Bridge ETH to BMC\n     * @dev Emit a {BuyBMC} event used for the bridge listener.\n     * Requirements:\n     *\n     * - Contract must not be paused\n     * - Transaction must be created through the frontend\n     *\n     * @param nonce transaction nonce\n     * @param amount amount of ETH to swap for BMC\n     * @param signature frontend transaction signature\n     */\n    function buyBMCWithETH(uint256 nonce, uint256 amount, bytes calldata signature) external payable;\n\n    /**\n     * @notice Bridge WBTC, USDT, USDC to BMC\n     * @dev Emit a {BuyBMC} event used for the bridge listener.\n     * Requirements:\n     *\n     * - Contract must not be paused\n     * - Token must be supported for bridge\n     * - User must have approved the contract to spend Token\n     * - Transaction must be created through the frontend\n     *\n     * @param nonce transaction nonce\n     * @param token address of Token to swap for BMC\n     * @param amount amount of Token to swap for BMC\n     * @param signature frontend transaction signature\n     */\n    function buyBMCWithToken(uint256 nonce, address token, uint256 amount, bytes calldata signature) external;\n\n    /**\n     * @notice Withdraw ETH funds from the contract\n     * @dev Requirements: Caller must be owner\n     * @param to address that will receive the funds\n     * @param amount amount to withdraw\n     */\n    function withdraw(address to, uint256 amount) external;\n\n    /**\n     * @notice Withdraw ERC20 funds from the contract\n     * @dev Requirements: Caller must be owner\n     * @param to address that will receive the funds\n     * @param amount amount to withdraw\n     */\n    function withdrawERC20(address token, address to, uint256 amount) external;\n\n    /**\n     * @notice Assign a new signer for the buyBMC transactions\n     * @dev Requirements: Caller must be owner\n     * @param _signer frontend address that will sign the buyBMC transactions\n     */\n    function newSigner(address _signer) external;\n\n    function getNextNonce() external view returns (uint256);\n\n    function erc20Balance(address token) external view returns (uint256);\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "evmVersion": "paris",
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}