S Price: $0.868213 (+0.29%)

Contract Diff Checker

Contract Name:
BridgeToken

Contract Source Code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./sources/OZ/Ownable.sol";
import "./BridgeToken.sol";
import "./chiefValidator.sol";

contract EmoliumTokenFactory is Ownable {
    struct TokenInfo {
        address tokenAddress;
        address initialOwner;
        string name;
        string symbol;
    }

    TokenInfo[] public deployedTokens; // Deployed Emolium Liquid Tokens

    event TokenDeployed(address indexed initialOwner, address indexed tokenAddress, string name, string symbol);

    address payable public chiefValidator; // Emolium ChiefValidator address
    address public chainRouter; // Emolium ChainRouter address

    constructor(address initialOwner, address payable _chiefValidatorAddress, address _chainRouterAddress)
        Ownable(initialOwner)
    {
        transferOwnership(initialOwner);
        chiefValidator = _chiefValidatorAddress;
        chainRouter = _chainRouterAddress;
    }

    // Deploy Liquid token
    function deployLiquidToken(string[] calldata namesAndSymbols, address initialOwner) external onlyOwner {
        require(namesAndSymbols.length % 2 == 0, "Input array must have an even length");
        require(initialOwner != address(0), "Invalid initialOwner address");
        require(chainRouter != address(0), "Invalid ChainRouter address");

        for (uint256 i = 0; i < namesAndSymbols.length; i += 2) {
            string memory name = namesAndSymbols[i];
            string memory symbol = namesAndSymbols[i + 1];

            BridgeToken newToken = new BridgeToken(name, symbol, initialOwner, chainRouter);

            deployedTokens.push(TokenInfo(address(newToken), initialOwner, name, symbol));

            emit TokenDeployed(initialOwner, address(newToken), name, symbol);
        }
    }

    // Register Liquid Tokens in ChiefValidator
    function registerTokens() external onlyOwner {
        require(chiefValidator != address(0), "ChiefValidator address not set");

        address[] memory tokenAddresses = new address[](deployedTokens.length);
        for (uint256 i = 0; i < deployedTokens.length; i++) {
            tokenAddresses[i] = deployedTokens[i].tokenAddress;
        }
        
        ChiefValidator(chiefValidator).registerBridgeTokens(tokenAddresses);
    }

    // Function to get the list of deployed tokens
    function getDeployedTokens() external view returns (TokenInfo[] memory) {
        return deployedTokens;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./sources/OZ/Ownable.sol";
import "./sources/OZ/ReentrancyGuard.sol";
import "./sources/OZ/IERC20.sol";
import "./sources/OZ/Address.sol";
import "./sources/IWETH.sol";
import "./sources/IMintableERC20.sol";
import "./sources/IBridgedToken.sol";
import "./validatorFactory.sol";
import "./validator.sol";


interface IBurnableERC20 {
    function burn(uint256 amount) external;
}
contract ChiefValidator is Ownable, ReentrancyGuard {
    address public EMLTokenAddress;
    ValidatorFactory public validatorFactory;
    uint256 public emlRewardAmount;
    address public bridgeDeposit;
    address public wethAddress;
    address public devAddress;
    IWETH public weth;
    
    // Retreive Bridge Tokens
    mapping(address => bool) public bridgeTokens;
    // Store Proofs
    mapping(bytes32 => bool) public proofs; // bool => true (legit) or false (not submitted or fraud)
    // Mapping of authorized Contracts
    mapping(address => bool) public authorizedContracts;

    // Events
    event TokenDeployed(address indexed initialOwner, address indexed tokenAddress, string name, string symbol);
    event ProofVerified(bytes32 indexed proofHash, bool valid);
    event EMLMinted(address indexed validator, uint256 amount);
    event BridgeTokenRegistered(address indexed tokenAddress);
    event BridgedETH(address to, uint256 amount);
    event BridgeFee(address to, uint256 amount);
    event TokenBridgedIn(address indexed tokenAddress, address to, uint256 amount);
    event BridgedTokenTransfered(address indexed tokenAddress, address to, uint256 amount);
    event DeployerAuthorized(address indexed contractAddress, bool isAuthorized);

    // Modifier
    modifier onlyAuthorized() {
        require(authorizedContracts[msg.sender], "Caller is not authorized");
        _;
    }

    constructor(
        address _EMLTokenAddress,
        address _wethAddress,
        address _devAddress,
        address _initialOwner,
        address _validatorFactory
    ) Ownable(_initialOwner) 
    {
        EMLTokenAddress = _EMLTokenAddress;
        validatorFactory = ValidatorFactory(_validatorFactory);
        wethAddress = _wethAddress;
        devAddress = _devAddress;
        transferOwnership(_initialOwner);
    }

    // Autorize contracts for validator creation
    function setAuthorization(address _contract, bool _isAuthorized) external onlyOwner {
        authorizedContracts[_contract] = _isAuthorized;
        emit DeployerAuthorized(_contract, _isAuthorized);
    }

    // Register Bridge Tokens
    function registerBridgeTokens(address[] calldata tokens) external onlyAuthorized {
        for (uint256 i = 0; i < tokens.length; i++) {
            bridgeTokens[tokens[i]] = true;
            emit BridgeTokenRegistered(tokens[i]);
        }
    }

    // Update Bridge Tokens
    function updateBridgeTokens(address tokenAddress, bool isRegistered) external onlyOwner {
        bridgeTokens[tokenAddress] = isRegistered;
    }

    function isBridgeTokenRegistered(address token) external view returns (bool) {
        return bridgeTokens[token];
    }
    
    // Verify Proof & Mint EML Rewards to validators
    function verifyProof(string[] calldata proofData, bytes32 proofHash) external nonReentrant onlyOwner {
        require(proofData.length == 6, "Invalid proof data length");
        require(!proofs[proofHash], "Proof already submitted");

        address[] memory validators = validatorFactory.getDeployedValidators();
        uint256 activeValidatorCount = 0;
        address[] memory activeValidators = new address[](validators.length);

        for (uint256 i = 0; i < validators.length; i++) {
            if (Validator(validators[i]).isActive()) {
                activeValidators[activeValidatorCount] = validators[i];
                activeValidatorCount++;
            }
        }

        require(activeValidatorCount > 0, "No active validators found");

        bool proofValid = false;
        for (uint256 i = 0; i < activeValidatorCount; i++) {
            bytes32 keccakResult = Validator(activeValidators[i]).calculateKeccak256(proofData);
            if (keccakResult == proofHash) {
                proofValid = true;
                break;
            }
        }

        if (proofValid) {
            mintEMLRewards();
            handleBridgedToken(proofData);
            proofs[proofHash] = true;
            emit ProofVerified(proofHash, true);
        } else {
            emit ProofVerified(proofHash, false);
        }
    }

    // Mint EML Rewards
    function mintEMLRewards() internal {
        address[] memory validators = validatorFactory.getDeployedValidators();
        uint256 totalActiveValidators = 0;

        // Number of active Validators
        for (uint256 i = 0; i < validators.length; i++) {
            if (Validator(validators[i]).isActive()) {
                totalActiveValidators++;
            }
        }

        require(totalActiveValidators > 0, "No active validators found");

        uint256 rewardPerValidator = emlRewardAmount / totalActiveValidators;

        // EML Mint to Active Validators only & equally 
        for (uint256 i = 0; i < validators.length; i++) {
            if (Validator(validators[i]).isActive()) {
                address validatorOwner = Validator(validators[i]).getValidatorOwner();
                // Call EML Mint function
                IMintableERC20(EMLTokenAddress).mint(validatorOwner, rewardPerValidator);
                emit EMLMinted(validatorOwner, rewardPerValidator);
            }
        }
    }

    // Handle Bridge Operations
    function handleBridgedToken(string[] calldata proofData) internal {
        address tokenAddress = parseAddress(proofData[0]);
        uint256 amount = parseUint(proofData[1]);
        address destination = parseAddress(proofData[2]);
        uint256 timestamp = parseUint(proofData[3]);
        string memory emoliumSaltData = proofData[4];
        uint256 providedFee = parseUint(proofData[5]);

        require(timestamp >= 0, "Invalid timestamp");
        require(bytes(emoliumSaltData).length > 0, "Invalid salt data");
        require(providedFee >= 0, "Invalid fee");

        // Fee calculation and validation
        uint256 calculatedFee = (amount * providedFee) / 10000;
        uint256 netAmount = amount - calculatedFee;
        
        // Handle ETH & WETH
        if (tokenAddress == wethAddress) {
            IWETH(wethAddress).withdraw(amount);
            payable(devAddress).transfer(calculatedFee);
            payable(destination).transfer(netAmount);
            emit BridgedETH(destination, netAmount);
            if (calculatedFee > 0) emit BridgeFee(devAddress, calculatedFee);
        }
        // Handle Emolium Bridge Registered Token
        else if (bridgeTokens[tokenAddress]) {
            IBridgedToken(tokenAddress).mint(destination, netAmount);
            IBridgedToken(tokenAddress).mint(devAddress, calculatedFee);
            emit TokenBridgedIn(tokenAddress, destination, netAmount);
            if (calculatedFee > 0) emit BridgeFee(devAddress, calculatedFee);
        }
        // Handle all other ERC20 tokens
        else {
            IERC20(tokenAddress).transfer(destination, netAmount);
            IERC20(tokenAddress).transfer(devAddress, calculatedFee);
            emit BridgedTokenTransfered(tokenAddress, destination, netAmount);
            if (calculatedFee > 0) emit BridgeFee(devAddress, calculatedFee);
        }
    }

    // Convert string to uint
    function parseUint(string memory _value) internal pure returns (uint256) {
        bytes memory b = bytes(_value);
        uint256 number = 0;
        for (uint256 i = 0; i < b.length; i++) {
            number = number * 10 + (uint256(uint8(b[i])) - 48);
        }
        return number;
    }

    // Convert string to address
    function parseAddress(string memory _value) internal pure returns (address) {
        bytes memory b = bytes(_value);
        require(b.length == 42, "Invalid address length"); // Inclut '0x' au début

        uint160 number;
        for (uint256 i = 2; i < b.length; i++) { // Skip '0x'
            number *= 16;
            uint8 c = uint8(b[i]);
            if (c >= 48 && c <= 57) {
                number += c - 48;
            } else if (c >= 97 && c <= 102) {
                number += c - 87;
            } else if (c >= 65 && c <= 70) {
                number += c - 55;
            }
        }
        return address(number);
    }

    // Setters for addresses
    function setBridgeDeposit(address _bridgeDeposit) external onlyOwner {
        bridgeDeposit = _bridgeDeposit;
    }

    function setWethAddress(address _wethAddress) external onlyOwner {
        wethAddress = _wethAddress;
    }

    // Set EML Reward Amount
    function setEMLRewardAmount(uint256 _amount) external onlyOwner {
        emlRewardAmount = _amount;
    }

    // Get Proof Status
    function getProofStatus(bytes32 proofHash) external view returns (bool) {
        return proofs[proofHash];
    }

    // Emergency & Rescue Functions
    function rescueETH() external onlyOwner {
        require(address(this).balance > 0, "Insufficient ETH balance");
        payable(devAddress).transfer(address(this).balance);
    }

    function rescueERC20(address token, uint256 amount) external onlyOwner {
        IERC20 erc20 = IERC20(token);
        require(erc20.balanceOf(address(this)) >= amount, "Insufficient token balance");
        erc20.transfer(devAddress, amount);
    }

    function rescueWETH(uint256 amount) external onlyOwner {
        require(weth.balanceOf(address(this)) >= amount, "Insufficient WETH balance");
        weth.transfer(devAddress, amount);
    }

    receive() external payable {}

}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./sources/OZ/ERC20.sol";
import "./sources/OZ/Ownable.sol";
import "./sources/OZ/ERC20Burnable.sol";
import "./sources/OZ/Pausable.sol";

interface IChainRouter {
    function getChainInfo(uint256 networkId) external view returns (string memory, uint256);
}

contract BridgeToken is ERC20, ERC20Burnable, Ownable, Pausable {
    uint256 public initialSupply = 0 * 10**18; // No supply at deployment

    // Authorized minters and burners
    mapping(address => bool) public authorizedMinters;
    mapping(address => bool) public authorizedBurners;

    // Reference to the ChainRouter contract
    IChainRouter public chainRouter;

    event MinterAdded(address indexed minter);
    event MinterRemoved(address indexed minter);
    event BurnerAdded(address indexed burner);
    event BurnerRemoved(address indexed burner);

    // The constructor takes the initial owner address and calls the Ownable constructor
    constructor (
        string memory _name, 
        string memory _symbol, 
        address initialOwner,
        address chainRouterAddress
    )
    ERC20 (_name, _symbol)
    Ownable (initialOwner)
    {
        _mint(initialOwner, initialSupply);
        chainRouter = IChainRouter(chainRouterAddress); // Initialize ChainRouter contract address
    }

    // Modifiers
    modifier onlyAuthorizedMinter() {
        require(authorizedMinters[msg.sender], "Not an authorized minter");
        _;
    }

    modifier onlyAuthorizedBurner() {
        require(authorizedBurners[msg.sender], "Not an authorized burner");
        _;
    }

    function addMinter(address minter) external onlyOwner {
        authorizedMinters[minter] = true;
        emit MinterAdded(minter);
    }

    function removeMinter(address minter) external onlyOwner {
        authorizedMinters[minter] = false;
        emit MinterRemoved(minter);
    }

    function addBurner(address burner) external onlyOwner {
        authorizedBurners[burner] = true;
        emit BurnerAdded(burner);
    }

    function removeBurner(address burner) external onlyOwner {
        authorizedBurners[burner] = false;
        emit BurnerRemoved(burner);
    }

    function mint(address to, uint256 amount) external onlyAuthorizedMinter whenNotPaused {
        _mint(to, amount);
    }

    function burn(uint256 amount) public override onlyAuthorizedBurner whenNotPaused {
        super.burn(amount);
    }

    function burnFrom(address account, uint256 amount) public override onlyAuthorizedBurner whenNotPaused {
        super.burnFrom(account, amount);
    }

    // Function to get the name of a registered blockchain from its ID (using ChainRouter)
    function getBlockchainById(uint256 networkId) external view returns (string memory) {
        // Fetch blockchain info from ChainRouter
        (string memory chainName, ) = chainRouter.getChainInfo(networkId);
        return chainName;
    }

    function pause() external onlyOwner {_pause();}
    
    function unpause() external onlyOwner {_unpause();}

    function transfer(address recipient, uint256 amount) public override whenNotPaused returns (bool) {
        return super.transfer(recipient, amount);
    }

    function transferFrom(address sender, address recipient, uint256 amount) public override whenNotPaused returns (bool) {
        return super.transferFrom(sender, recipient, amount);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import  "./Context.sol";

abstract contract Ownable is Context {
    address private _owner;

    error OwnableUnauthorizedAccount(address account);
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    function owner() public view virtual returns (address) {
        return _owner;
    }

    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./sources/OZ/IERC20.sol";
import "./sources/OZ/Ownable.sol";

contract Validator is Ownable {
    address public EMLTokenAddress;
    address public validatorFactory;
    uint256 public validatorPrice;
    address public admin;
    bool public active = false;

    // Validator Current Owner
    mapping(address => address) public validatorOwners;

    // Mapping to associate the contract address with the validator address
    mapping(address => address) public validatorContracts;
    // Events
    event EMLBalanceChecked(uint256 balance);
    event ValidatorCount(uint256 activeCount, uint256 maxCount);
    event ValidatorClosed(address indexed owner, uint256 amount);
    event ValidatorOpened();
    event ValidatorClosedEvent();

    constructor(address _EMLTokenAddress, address _initialOwner, address _validatorFactory) Ownable(_initialOwner) {
        EMLTokenAddress = _EMLTokenAddress;
        admin = _initialOwner;
        validatorFactory = _validatorFactory; // Set the validatorFactory by default
        validatorContracts[address(this)] = address(this);
        validatorOwners[address(this)] = _initialOwner;
    }
    
    // Keccak hash function for an array of strings
    function calculateKeccak256(string[] calldata inputs) external view returns (bytes32) {
        require(active, "Validator is not active"); // Check if the validator is active

        // Concatenate all strings in the array
        bytes memory concatenatedString;
        for (uint256 i = 0; i < inputs.length; i++) {
            concatenatedString = abi.encodePacked(concatenatedString, inputs[i]);
        }

        // Calculate the Keccak256 hash of the concatenated string
        bytes32 hash = keccak256(concatenatedString);
        return hash;
    }

    // Set a new validator factory
    function setValidatorFactory(address _validatorFactory) external onlyOwner {
        require(_validatorFactory != address(0), "Invalid validator factory address");
        validatorFactory = _validatorFactory;
    }

    // Return the validator contract address
    function getValidatorContractAddress() external view returns (address) {
        return validatorContracts[address(this)];
    }

    // Return the current validator owner
    function getValidatorOwner() external view returns (address) {
        return validatorOwners[address(this)];
    }

    // Return the amount of EML available in the contract
    function getEMLBalance() external view returns (uint256) {
        return IERC20(EMLTokenAddress).balanceOf(address(this));
    }

    // Function to close the validator and send the EML balance to the owner
    function closeValidator() external onlyOwner {
        uint256 balance = IERC20(EMLTokenAddress).balanceOf(address(this));
        require(balance > 0, "No EML tokens to transfer");

        IERC20(EMLTokenAddress).transfer(owner(), balance);
        active = false;

        emit ValidatorClosed(owner(), balance);
        emit ValidatorClosedEvent();
    }

    // Open the validator if it holds at least 20,000 EML
    function openValidator() external onlyOwner {
        uint256 emlBalance = IERC20(EMLTokenAddress).balanceOf(address(this));
        emit EMLBalanceChecked(emlBalance); // Log la balance

        require(emlBalance >= 20000 * 10**18, "Validator must hold at least 20,000 EML");
        active = true;
        emit ValidatorOpened();
    }

    // Function to check if the validator is active
    function isActive() external view returns (bool) {
        return active;
    }

    // Function to get the admin address
    function getAdmin() external view returns (address) {
        return admin;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./sources/OZ/Ownable.sol";
import "./validator.sol";

contract ValidatorFactory is Ownable {

    // Mapping to store the addresses of deployed validators
    mapping(address => bool) public validators;
    // Mapping of authorized Contracts
    mapping(address => bool) public authorizedContracts;
    // List of deployed validators
    address[] public deployedValidators;
    // Maximum number of active validators
    uint256 public constant MAX_ACTIVE_VALIDATORS = 25;

    // Events
    event ValidatorCreated(address indexed validatorAddress);
    event ContractAuthorized(address indexed contractAddress, bool isAuthorized);

    // Modifier
    modifier onlyAuthorized() {
        require(authorizedContracts[msg.sender], "Caller is not authorized");
        _;
    }

    constructor(address _initialOwner) Ownable(_initialOwner) {
    } 

    // Autorize contracts for validator creation
    function setAuthorization(address _contract, bool _isAuthorized) external onlyOwner {
        authorizedContracts[_contract] = _isAuthorized;
        emit ContractAuthorized(_contract, _isAuthorized);
    }

    // Function to create a single Validator and assign ownership to the provided owner
    function createValidator(address _EMLTokenAddress, address _owner) external onlyAuthorized returns (address) {
        // Check the number of active validators
        uint256 activeValidatorCount = 0;
        for (uint256 i = 0; i < deployedValidators.length; i++) {
            if (Validator(deployedValidators[i]).isActive()) {
                activeValidatorCount++;
            }
        }

        require(
            activeValidatorCount < MAX_ACTIVE_VALIDATORS,
            "Maximum number of active validators reached"
        );

        // Create the validator and pass this factory address in the constructor
        Validator newValidator = new Validator(_EMLTokenAddress, _owner, address(this));

        // Register the new validator in the factory
        address validatorAddress = address(newValidator);
        validators[validatorAddress] = true;
        deployedValidators.push(validatorAddress);

        emit ValidatorCreated(validatorAddress);
        return validatorAddress;
    }

    // Create a batch of Validators
    function createInitialValidators(address _EMLTokenAddress, uint256 numberOfValidators) external onlyOwner {
        require(numberOfValidators > 0, "Number of validators must be greater than 0");
        require(numberOfValidators <= MAX_ACTIVE_VALIDATORS, "Cannot create more than the maximum allowed validators");
        
        // Check active Validators number
        uint256 activeValidatorCount = 0;
        for (uint256 i = 0; i < deployedValidators.length; i++) {
            if (Validator(deployedValidators[i]).isActive()) {
                activeValidatorCount++;
            }
        }

        require(
            deployedValidators.length + numberOfValidators <= MAX_ACTIVE_VALIDATORS || activeValidatorCount < MAX_ACTIVE_VALIDATORS,
            "Cannot exceed the maximum number of validators"
        );

        for (uint256 i = 0; i < numberOfValidators; i++) {
            Validator newValidator = new Validator(_EMLTokenAddress, msg.sender, address(this));
            address validatorAddress = address(newValidator);
            
            validators[validatorAddress] = true;
            deployedValidators.push(validatorAddress);

            emit ValidatorCreated(validatorAddress);
        }
    }

    // Check if a validator is active
    function isValidatorActive(address validatorAddress) external view returns (bool) {
        require(validators[validatorAddress], "Validator not found");
        return Validator(validatorAddress).isActive();
    }

    // Get the owner of a validator
    function getValidatorOwner(address validatorAddress) external view returns (address) {
        require(validators[validatorAddress], "Validator not found");
        return Validator(validatorAddress).getValidatorOwner();
    }

    // Get the list of deployed validators
    function getDeployedValidators() external view returns (address[] memory) {
        return deployedValidators;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IBridgedToken {
    function mint(address to, uint256 amount) external;
    function burn(address from, uint256 amount) external;
    function burnFrom(address from, uint256 amount) external;
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function decimals() external view returns (uint8);
    function symbol() external view returns (string memory);
    function name() external view returns (string memory);
    function tokenAddress() external view returns (address);
    function mintable() external view returns (bool);
    function burnable() external view returns (bool);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IMintableERC20 {
    function mint(address to, uint256 amount) external;
    function burn(address from, uint256 amount) external;
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function decimals() external view returns (uint8);
    function symbol() external view returns (string memory);
    function name() external view returns (string memory);
    function tokenAddress() external view returns (address);
    function mintable() external view returns (bool);
    function burnable() external view returns (bool);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IWETH {
    function deposit() external payable;
    function withdraw(uint256 wad) external;
    function balanceOf(address guy) external view returns (uint256);
    function transfer(address dst, uint256 wad) external returns (bool);
    function approve(address usr, uint256 wad) external returns (bool);
    function transferFrom(address src, address dst, uint256 wad) external returns (bool);
    function allowance(address src, address dst) external view returns (uint256);
    event Deposit(address indexed dst, uint256 wad);
    event Withdrawal(address indexed src, uint256 wad);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

import "./Errors.sol";

library Address {
    error AddressEmptyCode(address target);
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert Errors.InsufficientBalance(address(this).balance, amount);
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert Errors.FailedCall();
        }
    }

    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            assembly ("memory-safe") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

interface IERC20 {

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    
    function totalSupply() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address to, uint256 value) external returns (bool);

    function allowance(address owner, address spender) external view returns (uint256);

    function approve(address spender, uint256 value) external returns (bool);

    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

abstract contract ReentrancyGuard {

    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;
    uint256 private _status;

    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }
    
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        _status = NOT_ENTERED;
    }

    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {Context} from "./Context.sol";

abstract contract Pausable is Context {
    bool private _paused;

    event Paused(address account);
    event Unpaused(address account);

    error EnforcedPause();
    error ExpectedPause();

    constructor() {
        _paused = false;
    }

    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    modifier whenPaused() {
        _requirePaused();
        _;
    }

    function paused() public view virtual returns (bool) {
        return _paused;
    }

    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Burnable.sol)

pragma solidity ^0.8.20;

import "./ERC20.sol";
import "./Context.sol";

abstract contract ERC20Burnable is Context, ERC20 {

    function burn(uint256 value) public virtual {
        _burn(_msgSender(), value);
    }

    function burnFrom(address account, uint256 value) public virtual {
        _spendAllowance(account, _msgSender(), value);
        _burn(account, value);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import "./IERC20.sol";
import "./IERC20Metadata.sol";
import "./Context.sol";
import "./draft-IERC6093.sol";

abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {

    mapping(address account => uint256) private _balances;
    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    function name() public view virtual returns (string memory) {
        return _name;
    }

    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                _totalSupply -= value;
            }
        } else {
            unchecked {
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

interface IERC20Errors {

    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
    error ERC20InvalidSender(address sender);
    error ERC20InvalidReceiver(address receiver);
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
    error ERC20InvalidApprover(address approver);
    error ERC20InvalidSpender(address spender);
}

interface IERC721Errors {

    error ERC721InvalidOwner(address owner);
    error ERC721NonexistentToken(uint256 tokenId);
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
    error ERC721InvalidSender(address sender);
    error ERC721InvalidReceiver(address receiver);
    error ERC721InsufficientApproval(address operator, uint256 tokenId);
    error ERC721InvalidApprover(address approver);
    error ERC721InvalidOperator(address operator);
}

interface IERC1155Errors {

    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
    error ERC1155InvalidSender(address sender);
    error ERC1155InvalidReceiver(address receiver);
    error ERC1155MissingApprovalForAll(address operator, address owner);
    error ERC1155InvalidApprover(address approver);
    error ERC1155InvalidOperator(address operator);
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import "./IERC20.sol";

interface IERC20Metadata is IERC20 {
    
    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function decimals() external view returns (uint8);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

library Errors {
    
    error InsufficientBalance(uint256 balance, uint256 needed);
    error FailedCall();
    error FailedDeployment();
    error MissingPrecompile(address);
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):