{"file_path":"contracts/MintingHub.sol","creation_status":"success","source_code":"// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.0;\r\n\r\nimport \"./interface/IERC20.sol\";\r\nimport \"./interface/IReserve.sol\";\r\nimport \"./interface/IFrankencoin.sol\";\r\nimport \"./interface/IPosition.sol\";\r\nimport \"./interface/IPositionFactory.sol\";\r\n\r\n/**\r\n * @title Minting Hub\r\n * @notice The central hub for creating, cloning and challenging collateralized Frankencoin positions.\r\n * @dev Only one instance of this contract is required, whereas every new position comes with a new position\r\n * contract. Pending challenges are stored as structs in an array.\r\n */\r\ncontract MintingHub {\r\n    /**\r\n     * @notice Irrevocable fee in ZCHF when proposing a new position (but not when cloning an existing one).\r\n     */\r\n    uint256 public constant OPENING_FEE = 1000 * 10 ** 18;\r\n\r\n    /**\r\n     * @notice The challenger reward in parts per million (ppm) relative to the challenged amount, whereas\r\n     * challenged amount if defined as the challenged collateral amount times the liquidation price.\r\n     */\r\n    uint32 public constant CHALLENGER_REWARD = 20000; // 2%\r\n\r\n    IPositionFactory private immutable POSITION_FACTORY; // position contract to clone\r\n\r\n    IFrankencoin public immutable zchf; // currency\r\n    Challenge[] public challenges; // list of open challenges\r\n\r\n    /**\r\n     * @notice Map to remember pending postponed collateral returns.\r\n     * @dev It maps collateral => beneficiary => amount.\r\n     */\r\n    mapping(address collateral => mapping(address owner => uint256 amount)) public pendingReturns;\r\n\r\n    struct Challenge {\r\n        address challenger; // the address from which the challenge was initiated\r\n        uint64 start; // the start of the challenge\r\n        IPosition position; // the position that was challenged\r\n        uint256 size; // how much collateral the challenger provided\r\n    }\r\n\r\n    event PositionOpened(\r\n        address indexed owner,\r\n        address indexed position,\r\n        address zchf,\r\n        address collateral,\r\n        uint256 price\r\n    );\r\n    event ChallengeStarted(address indexed challenger, address indexed position, uint256 size, uint256 number);\r\n    event ChallengeAverted(address indexed position, uint256 number, uint256 size);\r\n    event ChallengeSucceeded(\r\n        address indexed position,\r\n        uint256 number,\r\n        uint256 bid,\r\n        uint256 acquiredCollateral,\r\n        uint256 challengeSize\r\n    );\r\n    event PostPonedReturn(address collateral, address indexed beneficiary, uint256 amount);\r\n\r\n    error UnexpectedPrice();\r\n    error InvalidPos();\r\n\r\n    modifier validPos(address position) {\r\n        if (zchf.getPositionParent(position) != address(this)) revert InvalidPos();\r\n        _;\r\n    }\r\n\r\n    constructor(address _zchf, address _factory) {\r\n        zchf = IFrankencoin(_zchf);\r\n        POSITION_FACTORY = IPositionFactory(_factory);\r\n    }\r\n\r\n    function openPositionOneWeek(\r\n        address _collateralAddress,\r\n        uint256 _minCollateral,\r\n        uint256 _initialCollateral,\r\n        uint256 _mintingMaximum,\r\n        uint256 _expirationSeconds,\r\n        uint64 _challengeSeconds,\r\n        uint32 _annualInterestPPM,\r\n        uint256 _liqPrice,\r\n        uint32 _reservePPM\r\n    ) public returns (address) {\r\n        return\r\n            openPosition(\r\n                _collateralAddress,\r\n                _minCollateral,\r\n                _initialCollateral,\r\n                _mintingMaximum,\r\n                7 days,\r\n                _expirationSeconds,\r\n                _challengeSeconds,\r\n                _annualInterestPPM,\r\n                _liqPrice,\r\n                _reservePPM\r\n            );\r\n    }\r\n\r\n    /**\r\n     * @notice Open a collateralized loan position. See also https://docs.frankencoin.com/positions/open .\r\n     * @dev For a successful call, you must set an allowance for the collateral token, allowing\r\n     * the minting hub to transfer the initial collateral amount to the newly created position and to\r\n     * withdraw the fees.\r\n     *\r\n     * @param _collateralAddress        address of collateral token\r\n     * @param _minCollateral     minimum collateral required to prevent dust amounts\r\n     * @param _initialCollateral amount of initial collateral to be deposited\r\n     * @param _mintingMaximum    maximal amount of ZCHF that can be minted by the position owner\r\n     * @param _expirationSeconds position tenor in unit of timestamp (seconds) from 'now'\r\n     * @param _challengeSeconds  challenge period. Longer for less liquid collateral.\r\n     * @param _annualInterestPPM ppm of minted amount that is paid as fee for each year of duration\r\n     * @param _liqPrice          Liquidation price with (36 - token decimals) decimals,\r\n     *                           e.g. 18 decimals for an 18 dec collateral, 36 decs for a 0 dec collateral.\r\n     * @param _reservePPM        ppm of minted amount that is locked as borrower's reserve, e.g. 20%\r\n     * @return address           address of created position\r\n     */\r\n    function openPosition(\r\n        address _collateralAddress,\r\n        uint256 _minCollateral,\r\n        uint256 _initialCollateral,\r\n        uint256 _mintingMaximum,\r\n        uint256 _initPeriodSeconds,\r\n        uint256 _expirationSeconds,\r\n        uint64 _challengeSeconds,\r\n        uint32 _annualInterestPPM,\r\n        uint256 _liqPrice,\r\n        uint32 _reservePPM\r\n    ) public returns (address) {\r\n        require(_annualInterestPPM <= 1000000);\r\n        require(CHALLENGER_REWARD <= _reservePPM && _reservePPM <= 1000000);\r\n        require(IERC20(_collateralAddress).decimals() <= 24); // leaves 12 digits for price\r\n        require(_initialCollateral >= _minCollateral, \"must start with min col\");\r\n        require(_minCollateral * _liqPrice >= 5000 ether * 10 ** 18); // must start with at least 5000 ZCHF worth of collateral\r\n        IPosition pos = IPosition(\r\n            POSITION_FACTORY.createNewPosition(\r\n                msg.sender,\r\n                address(zchf),\r\n                _collateralAddress,\r\n                _minCollateral,\r\n                _mintingMaximum,\r\n                _initPeriodSeconds,\r\n                _expirationSeconds,\r\n                _challengeSeconds,\r\n                _annualInterestPPM,\r\n                _liqPrice,\r\n                _reservePPM\r\n            )\r\n        );\r\n        zchf.registerPosition(address(pos));\r\n        zchf.collectProfits(msg.sender, OPENING_FEE);\r\n        IERC20(_collateralAddress).transferFrom(msg.sender, address(pos), _initialCollateral);\r\n\r\n        emit PositionOpened(msg.sender, address(pos), address(zchf), _collateralAddress, _liqPrice);\r\n        return address(pos);\r\n    }\r\n\r\n    /**\r\n     * @notice Clones an existing position and immediately tries to mint the specified amount using the given collateral.\r\n     * @dev This needs an allowance to be set on the collateral contract such that the minting hub can get the collateral.\r\n     */\r\n    function clone(\r\n        address position,\r\n        uint256 _initialCollateral,\r\n        uint256 _initialMint,\r\n        uint256 expiration\r\n    ) public validPos(position) returns (address) {\r\n        IPosition existing = IPosition(position);\r\n        require(expiration <= IPosition(existing.original()).expiration());\r\n        existing.reduceLimitForClone(_initialMint);\r\n        address pos = POSITION_FACTORY.clonePosition(position);\r\n        zchf.registerPosition(pos);\r\n        IPosition(pos).initializeClone(msg.sender, existing.price(), _initialCollateral, _initialMint, expiration);\r\n        existing.collateral().transferFrom(msg.sender, pos, _initialCollateral);\r\n\r\n        emit PositionOpened(\r\n            msg.sender,\r\n            address(pos),\r\n            address(zchf),\r\n            address(IPosition(pos).collateral()),\r\n            IPosition(pos).price()\r\n        );\r\n        return address(pos);\r\n    }\r\n\r\n    /**\r\n     * @notice Launch a challenge (Dutch auction) on a position\r\n     * @param _positionAddr      address of the position we want to challenge\r\n     * @param _collateralAmount  amount of the collateral we want to challenge\r\n     * @param expectedPrice      position.price() to guard against the minter fruntrunning with a price change\r\n     * @return index of the challenge in challenge-array\r\n     */\r\n    function challenge(\r\n        address _positionAddr,\r\n        uint256 _collateralAmount,\r\n        uint256 expectedPrice\r\n    ) external validPos(_positionAddr) returns (uint256) {\r\n        IPosition position = IPosition(_positionAddr);\r\n        if (position.price() != expectedPrice) revert UnexpectedPrice();\r\n        IERC20(position.collateral()).transferFrom(msg.sender, address(this), _collateralAmount);\r\n        uint256 pos = challenges.length;\r\n        challenges.push(Challenge(msg.sender, uint64(block.timestamp), position, _collateralAmount));\r\n        position.notifyChallengeStarted(_collateralAmount);\r\n        emit ChallengeStarted(msg.sender, address(position), _collateralAmount, pos);\r\n        return pos;\r\n    }\r\n\r\n    /**\r\n     * @notice Post a bid in ZCHF given an open challenge.\r\n     *\r\n     * @dev In case that the collateral cannot be transfered back to the challenger (i.e. because the collateral token\r\n     * has a blacklist and the challenger is on it), it is possible to postpone the return of the collateral.\r\n     *\r\n     * @param _challengeNumber  index of the challenge as broadcast in the event\r\n     * @param size              how much of the collateral the caller wants to bid for at most\r\n     *                          (automatically reduced to the available amount)\r\n     * @param postponeCollateralReturn To postpone the return of the collateral to the challenger. Usually false.\r\n     */\r\n    function bid(uint32 _challengeNumber, uint256 size, bool postponeCollateralReturn) external {\r\n        Challenge memory _challenge = challenges[_challengeNumber];\r\n        (uint256 liqPrice, uint64 phase1, uint64 phase2) = _challenge.position.challengeData(_challenge.start);\r\n        size = _challenge.size < size ? _challenge.size : size; // cannot bid for more than the size of the challenge\r\n\r\n        if (block.timestamp <= _challenge.start + phase1) {\r\n            _avertChallenge(_challenge, _challengeNumber, liqPrice, size);\r\n            emit ChallengeAverted(address(_challenge.position), _challengeNumber, size);\r\n        } else {\r\n            _returnChallengerCollateral(_challenge, _challengeNumber, size, postponeCollateralReturn);\r\n            (uint256 transferredCollateral, uint256 offer) = _finishChallenge(\r\n                _challenge,\r\n                liqPrice,\r\n                phase1,\r\n                phase2,\r\n                size\r\n            );\r\n            emit ChallengeSucceeded(address(_challenge.position), _challengeNumber, offer, transferredCollateral, size);\r\n        }\r\n    }\r\n\r\n    function _finishChallenge(\r\n        Challenge memory _challenge,\r\n        uint256 liqPrice,\r\n        uint64 phase1,\r\n        uint64 phase2,\r\n        uint256 size\r\n    ) internal returns (uint256, uint256) {\r\n        // Repayments depend on what was actually minted, whereas bids depend on the available collateral\r\n        (address owner, uint256 collateral, uint256 repayment, uint32 reservePPM) = _challenge\r\n            .position\r\n            .notifyChallengeSucceeded(msg.sender, size);\r\n\r\n        // No overflow possible thanks to invariant (col * price <= limit * 10**18)\r\n        // enforced in Position.setPrice and knowing that collateral <= col.\r\n        uint256 offer = (_calculatePrice(_challenge.start + phase1, phase2, liqPrice) * collateral) / 10 ** 18;\r\n        zchf.transferFrom(msg.sender, address(this), offer); // get money from bidder\r\n        uint256 reward = (offer * CHALLENGER_REWARD) / 1000_000;\r\n        zchf.transfer(_challenge.challenger, reward); // pay out the challenger reward\r\n        uint256 fundsAvailable = offer - reward; // funds available after reward\r\n\r\n        // Example: available funds are 90, repayment is 50, reserve 20%. Then 20%*(90-50)=16 are collected as profits\r\n        // and the remaining 34 are sent to the position owner. If the position owner maxed out debt before the challenge\r\n        // started and the liquidation price was 100, they would be slightly better off as they would get away with 80\r\n        // instead of 40+36 = 76 in this example.\r\n        if (fundsAvailable > repayment) {\r\n            // The excess amount is distributed between the system and the owner using the reserve ratio\r\n            // At this point, we cannot rely on the liquidation price because the challenge might have been started as a\r\n            // response to an unreasonable increase of the liquidation price, such that we have to use this heuristic\r\n            // for excess fund distribution, which make position owners that maxed out their positions slightly better\r\n            // off in comparison to those who did not.\r\n            uint256 profits = reservePPM * (fundsAvailable - repayment) / 1000_000;\r\n            zchf.collectProfits(address(this), profits);\r\n            zchf.transfer(owner, fundsAvailable - repayment - profits);\r\n        } else if (fundsAvailable < repayment) {\r\n            zchf.coverLoss(address(this), repayment - fundsAvailable); // ensure we have enough to pay everything\r\n        }\r\n        zchf.burnWithoutReserve(repayment, reservePPM); // Repay the challenged part, example: 50 ZCHF leading to 10 ZCHf in implicit profits\r\n        return (collateral, offer);\r\n    }\r\n\r\n    function _avertChallenge(Challenge memory _challenge, uint32 number, uint256 liqPrice, uint256 size) internal {\r\n        require(block.timestamp != _challenge.start); // do not allow to avert the challenge in the same transaction, see CS-ZCHF-037\r\n        if (msg.sender == _challenge.challenger) {\r\n            // allow challenger to cancel challenge without paying themselves\r\n        } else {\r\n            zchf.transferFrom(msg.sender, _challenge.challenger, (size * liqPrice) / (10 ** 18));\r\n        }\r\n\r\n        _challenge.position.notifyChallengeAverted(size);\r\n        _challenge.position.collateral().transfer(msg.sender, size);\r\n        if (size < _challenge.size) {\r\n            challenges[number].size = _challenge.size - size;\r\n        } else {\r\n            require(size == _challenge.size);\r\n            delete challenges[number];\r\n        }\r\n    }\r\n\r\n    /**\r\n     * @notice Returns 'amount' of the collateral to the challenger and reduces or deletes the relevant challenge.\r\n     */\r\n    function _returnChallengerCollateral(\r\n        Challenge memory _challenge,\r\n        uint32 number,\r\n        uint256 amount,\r\n        bool postpone\r\n    ) internal {\r\n        _returnCollateral(_challenge.position.collateral(), _challenge.challenger, amount, postpone);\r\n        if (_challenge.size == amount) {\r\n            // bid on full amount\r\n            delete challenges[number];\r\n        } else {\r\n            // bid on partial amount\r\n            challenges[number].size -= amount;\r\n        }\r\n    }\r\n\r\n    /**\r\n     * @notice Calculates the current Dutch auction price.\r\n     * @dev Starts at the full price at time 'start' and linearly goes to 0 as 'phase2' passes.\r\n     */\r\n    function _calculatePrice(uint64 start, uint64 phase2, uint256 liqPrice) internal view returns (uint256) {\r\n        uint64 timeNow = uint64(block.timestamp);\r\n        if (timeNow <= start) {\r\n            return liqPrice;\r\n        } else if (timeNow >= start + phase2) {\r\n            return 0;\r\n        } else {\r\n            uint256 timeLeft = phase2 - (timeNow - start);\r\n            return (liqPrice / phase2) * timeLeft;\r\n        }\r\n    }\r\n\r\n    /**\r\n     * @notice Get the price per unit of the collateral for the given challenge.\r\n     * @dev The price comes with (36-collateral.decimals()) digits, such that multiplying it with the\r\n     * raw collateral amount always yields a price with 36 digits, or 18 digits after dividing by 10**18 again.\r\n     */\r\n    function price(uint32 challengeNumber) public view returns (uint256) {\r\n        Challenge memory _challenge = challenges[challengeNumber];\r\n        if (_challenge.challenger == address(0x0)) {\r\n            return 0;\r\n        } else {\r\n            (uint256 liqPrice, uint64 phase1, uint64 phase2) = _challenge.position.challengeData(_challenge.start);\r\n            return _calculatePrice(_challenge.start + phase1, phase2, liqPrice);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * @notice Challengers can call this method to withdraw collateral whose return was postponed.\r\n     */\r\n    function returnPostponedCollateral(address collateral, address target) external {\r\n        uint256 amount = pendingReturns[collateral][msg.sender];\r\n        delete pendingReturns[collateral][msg.sender];\r\n        IERC20(collateral).transfer(target, amount);\r\n    }\r\n\r\n    function _returnCollateral(IERC20 collateral, address recipient, uint256 amount, bool postpone) internal {\r\n        if (postpone) {\r\n            // Postponing helps in case the challenger was blacklisted or otherwise cannot receive at the moment.\r\n            pendingReturns[address(collateral)][recipient] += amount;\r\n            emit PostPonedReturn(address(collateral), recipient, amount);\r\n        } else {\r\n            collateral.transfer(recipient, amount); // return the challenger's collateral\r\n        }\r\n    }\r\n}\r\n","deployed_bytecode":"0x608060405234801561001057600080fd5b50600436106100b45760003560e01c8063af5806b611610071578063af5806b6146101a7578063c14a9f05146101c5578063c4d4803a146101d8578063e6c99cdf146101ff578063e6ca1df214610212578063e85cde6f1461022557600080fd5b806306c697ef146100b95780632bf78dd8146100e95780635cb4791914610107578063643745fb1461011a5780637eb81bb3146101455780638f1d37761461015a575b600080fd5b6100cc6100c7366004611ecd565b610238565b6040516001600160a01b0390911681526020015b60405180910390f35b6100f9683635c9adc5dea0000081565b6040519081526020016100e0565b6100cc610115366004611f67565b61066b565b6100f9610128366004611fa2565b600160209081526000928352604080842090915290825290205481565b610158610153366004611fe9565b610c64565b005b61016d61016836600461202b565b610e67565b604080516001600160a01b0395861681526001600160401b03949094166020850152919093169082015260608101919091526080016100e0565b6101b0614e2081565b60405163ffffffff90911681526020016100e0565b6100f96101d3366004612044565b610ebb565b6100cc7f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb81565b6100cc61020d366004612079565b611273565b6100f9610220366004612108565b611298565b610158610233366004611fa2565b6113d4565b6000620f42408463ffffffff16111561025057600080fd5b63ffffffff8216614e20118015906102715750620f42408263ffffffff1611155b61027a57600080fd5b60188b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102de9190612125565b60ff1611156102ec57600080fd5b898910156103405760405162461bcd60e51b815260206004820152601760248201527f6d7573742073746172742077697468206d696e20636f6c000000000000000000604482015260640160405180910390fd5b700eb194f8e1ae525fd5dcfab0800000000061035c848c61215e565b101561036757600080fd5b60405163c3fd1c3760e01b81523360048201526001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb811660248301528c81166044830152606482018c9052608482018a905260a4820189905260c482018890526001600160401b03871660e483015263ffffffff868116610104840152610124830186905284166101448301526000917f0000000000000000000000000cde500e6940931ed190ded77bb48640c94863929091169063c3fd1c3790610164016020604051808303816000875af115801561044c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610470919061217b565b60405163b52c696d60e01b81526001600160a01b0380831660048301529192507f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb9091169063b52c696d90602401600060405180830381600087803b1580156104d857600080fd5b505af11580156104ec573d6000803e3d6000fd5b505060405163375edc7760e11b81526001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb169250636ebdb8ee9150610547903390683635c9adc5dea0000090600401612198565b600060405180830381600087803b15801561056157600080fd5b505af1158015610575573d6000803e3d6000fd5b50506040516323b872dd60e01b81526001600160a01b038f1692506323b872dd91506105a990339085908f906004016121b1565b6020604051808303816000875af11580156105c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ec91906121d5565b50806001600160a01b0316336001600160a01b03167f591ede549d7e337ac63249acd2d7849532b0a686377bbf0b0cca6c8abd9552f27f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb8f88604051610654939291906121b1565b60405180910390a39b9a5050505050505050505050565b60405163aa5dd7f160e01b81526001600160a01b038581166004830152600091869130917f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb9091169063aa5dd7f190602401602060405180830381865afa1580156106da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fe919061217b565b6001600160a01b031614610725576040516369229ba760e01b815260040160405180910390fd5b6000869050806001600160a01b03166346c715fa6040518163ffffffff1660e01b81526004016020604051808303816000875af115801561076a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078e919061217b565b6001600160a01b0316634665096d6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156107cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107f191906121f2565b8411156107fd57600080fd5b604051635861764160e11b8152600481018690526001600160a01b0382169063b0c2ec8290602401600060405180830381600087803b15801561083f57600080fd5b505af1158015610853573d6000803e3d6000fd5b5050604051630167d12360e71b81526001600160a01b038a81166004830152600093507f0000000000000000000000000cde500e6940931ed190ded77bb48640c948639216915063b3e89180906024016020604051808303816000875af11580156108c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e6919061217b565b60405163b52c696d60e01b81526001600160a01b0380831660048301529192507f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb9091169063b52c696d90602401600060405180830381600087803b15801561094e57600080fd5b505af1158015610962573d6000803e3d6000fd5b50505050806001600160a01b031663aee1a95933846001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156109b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109da91906121f2565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604481018a9052606481018990526084810188905260a401600060405180830381600087803b158015610a3557600080fd5b505af1158015610a49573d6000803e3d6000fd5b50505050816001600160a01b031663d8dfeb456040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610a8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ab1919061217b565b6001600160a01b03166323b872dd33838a6040518463ffffffff1660e01b8152600401610ae0939291906121b1565b6020604051808303816000875af1158015610aff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b2391906121d5565b50806001600160a01b0316336001600160a01b03167f591ede549d7e337ac63249acd2d7849532b0a686377bbf0b0cca6c8abd9552f27f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb846001600160a01b031663d8dfeb456040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610bba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bde919061217b565b856001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610c1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4291906121f2565b604051610c51939291906121b1565b60405180910390a3979650505050505050565b6000808463ffffffff1681548110610c7e57610c7e61220b565b600091825260208083206040805160808101825260039490940290910180546001600160a01b038082168652600160a01b9091046001600160401b03169385018490526001820154168483018190526002909101546060850152905163d6e8f96b60e01b815260048101929092529193508291829163d6e8f96b90602401606060405180830381865afa158015610d19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3d9190612221565b92509250925085846060015110610d545785610d5a565b83606001515b9550818460200151610d6c9190612259565b6001600160401b03164211610ddb57610d878488858961146a565b604080850151815163ffffffff8a168152602081018990526001600160a01b03909116917f1eee30d91b773ac47d7485a3acb6bcd8c7c9cd8d95301b1af361baf5f0991d2e910160405180910390a2610e5e565b610de784888888611737565b600080610df7868686868c61184c565b604080890151815163ffffffff8e16815260208101849052918201849052606082018c90529294509092506001600160a01b03909116907f7d3a26e8d43c5b70f86266bfa26c212e3c097716ff7240ccb6a9034e48754e239060800160405180910390a250505b50505050505050565b60008181548110610e7757600080fd5b60009182526020909120600390910201805460018201546002909201546001600160a01b038083169450600160a01b9092046001600160401b031692919091169084565b60405163aa5dd7f160e01b81526001600160a01b038481166004830152600091859130917f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb9091169063aa5dd7f190602401602060405180830381865afa158015610f2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4e919061217b565b6001600160a01b031614610f75576040516369229ba760e01b815260040160405180910390fd5b600085905083816001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610fbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fdf91906121f2565b14610ffd57604051630d57523160e11b815260040160405180910390fd5b806001600160a01b031663d8dfeb456040518163ffffffff1660e01b81526004016020604051808303816000875af115801561103d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611061919061217b565b6001600160a01b03166323b872dd3330886040518463ffffffff1660e01b8152600401611090939291906121b1565b6020604051808303816000875af11580156110af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d391906121d5565b5060008054604080516080810182523381526001600160401b03428116602083019081526001600160a01b03878116848601818152606086018e8152600189018a559880529451600388027f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563810180549551909616600160a01b026001600160e01b0319909516918416919091179390931790935592517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56482018054919094166001600160a01b0319919091161790925593517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56590910155516342d0204160e01b81529091906342d02041906111f190899060040190815260200190565b600060405180830381600087803b15801561120b57600080fd5b505af115801561121f573d6000803e3d6000fd5b505060408051898152602081018590526001600160a01b03861693503392507fc4b384b2c5ca32c8e77081f4083be594a1ea9ba34f208a9f9a458f70608585f5910160405180910390a39695505050505050565b600061128a8a8a8a8a62093a808b8b8b8b8b610238565b9a9950505050505050505050565b60008060008363ffffffff16815481106112b4576112b461220b565b60009182526020918290206040805160808101825260039390930290910180546001600160a01b03808216808652600160a01b9092046001600160401b03169585019590955260018201549094169183019190915260020154606082015291506113215750600092915050565b600080600083604001516001600160a01b031663d6e8f96b85602001516040518263ffffffff1660e01b815260040161136991906001600160401b0391909116815260200190565b606060405180830381865afa158015611386573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113aa9190612221565b9250925092506113ca8285602001516113c39190612259565b8285611cf2565b9695505050505050565b6001600160a01b038216600081815260016020908152604080832033845290915280822080549290555163a9059cbb60e01b815290919063a9059cbb906114219085908590600401612198565b6020604051808303816000875af1158015611440573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146491906121d5565b50505050565b83602001516001600160401b0316420361148357600080fd5b83516001600160a01b031633146115495783516001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb16906323b872dd903390670de0b6b3a76400006114dc878761215e565b6114e69190612280565b6040518463ffffffff1660e01b8152600401611504939291906121b1565b6020604051808303816000875af1158015611523573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154791906121d5565b505b83604001516001600160a01b03166349746f10826040518263ffffffff1660e01b815260040161157b91815260200190565b600060405180830381600087803b15801561159557600080fd5b505af11580156115a9573d6000803e3d6000fd5b5050505083604001516001600160a01b031663d8dfeb456040518163ffffffff1660e01b81526004016020604051808303816000875af11580156115f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611615919061217b565b6001600160a01b031663a9059cbb33836040518363ffffffff1660e01b8152600401611642929190612198565b6020604051808303816000875af1158015611661573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168591906121d5565b5083606001518110156116d5578084606001516116a291906122a2565b60008463ffffffff16815481106116bb576116bb61220b565b906000526020600020906003020160020181905550611464565b836060015181146116e557600080fd5b60008363ffffffff16815481106116fe576116fe61220b565b60009182526020822060039091020180546001600160e01b03191681556001810180546001600160a01b03191690556002015550505050565b6117ab84604001516001600160a01b031663d8dfeb456040518163ffffffff1660e01b81526004016020604051808303816000875af115801561177e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117a2919061217b565b85518484611d8a565b818460600151036118075760008363ffffffff16815481106117cf576117cf61220b565b60009182526020822060039091020180546001600160e01b03191681556001810180546001600160a01b031916905560020155611464565b8160008463ffffffff16815481106118215761182161220b565b9060005260206000209060030201600201600082825461184191906122a2565b909155505050505050565b6000806000806000808a604001516001600160a01b03166320aa9e7233896040518363ffffffff1660e01b8152600401611887929190612198565b6080604051808303816000875af11580156118a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ca91906122b5565b93509350935093506000670de0b6b3a7640000846118f88c8f602001516118f19190612259565b8c8f611cf2565b611902919061215e565b61190c9190612280565b6040516323b872dd60e01b81529091506001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb16906323b872dd9061195f903390309086906004016121b1565b6020604051808303816000875af115801561197e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a291906121d5565b506000620f42406119b5614e208461215e565b6119bf9190612280565b8d5160405163a9059cbb60e01b81529192506001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb169163a9059cbb91611a10918590600401612198565b6020604051808303816000875af1158015611a2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5391906121d5565b506000611a6082846122a2565b905084811115611bc5576000620f4240611a7a87846122a2565b611a8a9063ffffffff881661215e565b611a949190612280565b60405163375edc7760e11b81529091506001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb1690636ebdb8ee90611ae59030908590600401612198565b600060405180830381600087803b158015611aff57600080fd5b505af1158015611b13573d6000803e3d6000fd5b505050507f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb6001600160a01b031663a9059cbb89838986611b5491906122a2565b611b5e91906122a2565b6040518363ffffffff1660e01b8152600401611b7b929190612198565b6020604051808303816000875af1158015611b9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bbe91906121d5565b5050611c57565b84811015611c57576001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb1663d1fa5e9830611c0784896122a2565b6040518363ffffffff1660e01b8152600401611c24929190612198565b600060405180830381600087803b158015611c3e57600080fd5b505af1158015611c52573d6000803e3d6000fd5b505050505b6040516363b20c3360e11b81526004810186905263ffffffff851660248201527f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb6001600160a01b03169063c764186690604401600060405180830381600087803b158015611cc557600080fd5b505af1158015611cd9573d6000803e3d6000fd5b50979a5093985050505050505050509550959350505050565b6000426001600160401b0380861690821611611d115782915050611d83565b611d1b8486612259565b6001600160401b0316816001600160401b031610611d3d576000915050611d83565b6000611d498683612301565b611d539086612301565b6001600160401b0316905080856001600160401b031685611d749190612280565b611d7e919061215e565b925050505b9392505050565b8015611e16576001600160a01b03808516600090815260016020908152604080832093871683529290529081208054849290611dc7908490612321565b92505081905550826001600160a01b03167f8ab298b78a235f73eee230f82012c0cf4db76003eaabd16a0195f112e7d625c88584604051611e09929190612198565b60405180910390a2611464565b60405163a9059cbb60e01b81526001600160a01b0385169063a9059cbb90611e449086908690600401612198565b6020604051808303816000875af1158015611e63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e8791906121d5565b5050505050565b6001600160a01b0381168114611ea357600080fd5b50565b6001600160401b0381168114611ea357600080fd5b63ffffffff81168114611ea357600080fd5b6000806000806000806000806000806101408b8d031215611eed57600080fd5b8a35611ef881611e8e565b995060208b0135985060408b0135975060608b0135965060808b0135955060a08b0135945060c08b0135611f2b81611ea6565b935060e08b0135611f3b81611ebb565b92506101008b013591506101208b0135611f5481611ebb565b809150509295989b9194979a5092959850565b60008060008060808587031215611f7d57600080fd5b8435611f8881611e8e565b966020860135965060408601359560600135945092505050565b60008060408385031215611fb557600080fd5b8235611fc081611e8e565b91506020830135611fd081611e8e565b809150509250929050565b8015158114611ea357600080fd5b600080600060608486031215611ffe57600080fd5b833561200981611ebb565b925060208401359150604084013561202081611fdb565b809150509250925092565b60006020828403121561203d57600080fd5b5035919050565b60008060006060848603121561205957600080fd5b833561206481611e8e565b95602085013595506040909401359392505050565b60008060008060008060008060006101208a8c03121561209857600080fd5b89356120a381611e8e565b985060208a0135975060408a0135965060608a0135955060808a0135945060a08a01356120cf81611ea6565b935060c08a01356120df81611ebb565b925060e08a013591506101008a01356120f781611ebb565b809150509295985092959850929598565b60006020828403121561211a57600080fd5b8135611d8381611ebb565b60006020828403121561213757600080fd5b815160ff81168114611d8357600080fd5b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761217557612175612148565b92915050565b60006020828403121561218d57600080fd5b8151611d8381611e8e565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6000602082840312156121e757600080fd5b8151611d8381611fdb565b60006020828403121561220457600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b60008060006060848603121561223657600080fd5b83519250602084015161224881611ea6565b604085015190925061202081611ea6565b6001600160401b0381811683821601908082111561227957612279612148565b5092915050565b60008261229d57634e487b7160e01b600052601260045260246000fd5b500490565b8181038181111561217557612175612148565b600080600080608085870312156122cb57600080fd5b84516122d681611e8e565b80945050602085015192506040850151915060608501516122f681611ebb565b939692955090935050565b6001600160401b0382811682821603908082111561227957612279612148565b808201808211156121755761217561214856fea2646970667358221220d8b8ffc2fffb45af378c6dd602d0b56a09dfe51cd781980d51227bbb938d024f64736f6c63430008140033","optimization_enabled":true,"verified_twin_address_hash":null,"is_verified":true,"compiler_settings":{"evmVersion":"paris","libraries":{},"metadata":{"bytecodeHash":"ipfs","useLiteralContent":true},"optimizer":{"enabled":true,"runs":200},"remappings":[]},"optimization_runs":200,"sourcify_repo_url":"https://repo.sourcify.dev/contracts/full_match/1/0x7546762fdb1a6d9146b33960545C3f6394265219/","decoded_constructor_args":[["0xB58E61C3098d85632Df34EecfB899A1Ed80921cB",{"internalType":"address","name":"_zchf","type":"address"}],["0x0CDE500e6940931ED190ded77bb48640c9486392",{"internalType":"address","name":"_factory","type":"address"}]],"compiler_version":"0.8.20+commit.a1b79de6","is_verified_via_verifier_alliance":false,"verified_at":"2024-02-15T07:04:03.732686Z","implementations":[],"proxy_type":null,"external_libraries":[],"creation_bytecode":"0x60c06040523480156200001157600080fd5b50604051620024a1380380620024a1833981016040819052620000349162000069565b6001600160a01b0391821660a05216608052620000a1565b80516001600160a01b03811681146200006457600080fd5b919050565b600080604083850312156200007d57600080fd5b62000088836200004c565b915062000098602084016200004c565b90509250929050565b60805160a05161236a62000137600039600081816101dd01528181610384015281816104920152818161050501528181610624015281816106910152818161090801528181610b5b01528181610ee1015281816114a001528181611926015281816119db01528181611aae01528181611b1901528181611bd70152611c790152600081816104000152610878015261236a6000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c8063af5806b611610071578063af5806b6146101a7578063c14a9f05146101c5578063c4d4803a146101d8578063e6c99cdf146101ff578063e6ca1df214610212578063e85cde6f1461022557600080fd5b806306c697ef146100b95780632bf78dd8146100e95780635cb4791914610107578063643745fb1461011a5780637eb81bb3146101455780638f1d37761461015a575b600080fd5b6100cc6100c7366004611ecd565b610238565b6040516001600160a01b0390911681526020015b60405180910390f35b6100f9683635c9adc5dea0000081565b6040519081526020016100e0565b6100cc610115366004611f67565b61066b565b6100f9610128366004611fa2565b600160209081526000928352604080842090915290825290205481565b610158610153366004611fe9565b610c64565b005b61016d61016836600461202b565b610e67565b604080516001600160a01b0395861681526001600160401b03949094166020850152919093169082015260608101919091526080016100e0565b6101b0614e2081565b60405163ffffffff90911681526020016100e0565b6100f96101d3366004612044565b610ebb565b6100cc7f000000000000000000000000000000000000000000000000000000000000000081565b6100cc61020d366004612079565b611273565b6100f9610220366004612108565b611298565b610158610233366004611fa2565b6113d4565b6000620f42408463ffffffff16111561025057600080fd5b63ffffffff8216614e20118015906102715750620f42408263ffffffff1611155b61027a57600080fd5b60188b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102de9190612125565b60ff1611156102ec57600080fd5b898910156103405760405162461bcd60e51b815260206004820152601760248201527f6d7573742073746172742077697468206d696e20636f6c000000000000000000604482015260640160405180910390fd5b700eb194f8e1ae525fd5dcfab0800000000061035c848c61215e565b101561036757600080fd5b60405163c3fd1c3760e01b81523360048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660248301528c81166044830152606482018c9052608482018a905260a4820189905260c482018890526001600160401b03871660e483015263ffffffff868116610104840152610124830186905284166101448301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063c3fd1c3790610164016020604051808303816000875af115801561044c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610470919061217b565b60405163b52c696d60e01b81526001600160a01b0380831660048301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063b52c696d90602401600060405180830381600087803b1580156104d857600080fd5b505af11580156104ec573d6000803e3d6000fd5b505060405163375edc7760e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169250636ebdb8ee9150610547903390683635c9adc5dea0000090600401612198565b600060405180830381600087803b15801561056157600080fd5b505af1158015610575573d6000803e3d6000fd5b50506040516323b872dd60e01b81526001600160a01b038f1692506323b872dd91506105a990339085908f906004016121b1565b6020604051808303816000875af11580156105c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ec91906121d5565b50806001600160a01b0316336001600160a01b03167f591ede549d7e337ac63249acd2d7849532b0a686377bbf0b0cca6c8abd9552f27f00000000000000000000000000000000000000000000000000000000000000008f88604051610654939291906121b1565b60405180910390a39b9a5050505050505050505050565b60405163aa5dd7f160e01b81526001600160a01b038581166004830152600091869130917f00000000000000000000000000000000000000000000000000000000000000009091169063aa5dd7f190602401602060405180830381865afa1580156106da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fe919061217b565b6001600160a01b031614610725576040516369229ba760e01b815260040160405180910390fd5b6000869050806001600160a01b03166346c715fa6040518163ffffffff1660e01b81526004016020604051808303816000875af115801561076a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078e919061217b565b6001600160a01b0316634665096d6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156107cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107f191906121f2565b8411156107fd57600080fd5b604051635861764160e11b8152600481018690526001600160a01b0382169063b0c2ec8290602401600060405180830381600087803b15801561083f57600080fd5b505af1158015610853573d6000803e3d6000fd5b5050604051630167d12360e71b81526001600160a01b038a81166004830152600093507f000000000000000000000000000000000000000000000000000000000000000016915063b3e89180906024016020604051808303816000875af11580156108c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e6919061217b565b60405163b52c696d60e01b81526001600160a01b0380831660048301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063b52c696d90602401600060405180830381600087803b15801561094e57600080fd5b505af1158015610962573d6000803e3d6000fd5b50505050806001600160a01b031663aee1a95933846001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156109b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109da91906121f2565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604481018a9052606481018990526084810188905260a401600060405180830381600087803b158015610a3557600080fd5b505af1158015610a49573d6000803e3d6000fd5b50505050816001600160a01b031663d8dfeb456040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610a8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ab1919061217b565b6001600160a01b03166323b872dd33838a6040518463ffffffff1660e01b8152600401610ae0939291906121b1565b6020604051808303816000875af1158015610aff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b2391906121d5565b50806001600160a01b0316336001600160a01b03167f591ede549d7e337ac63249acd2d7849532b0a686377bbf0b0cca6c8abd9552f27f0000000000000000000000000000000000000000000000000000000000000000846001600160a01b031663d8dfeb456040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610bba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bde919061217b565b856001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610c1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4291906121f2565b604051610c51939291906121b1565b60405180910390a3979650505050505050565b6000808463ffffffff1681548110610c7e57610c7e61220b565b600091825260208083206040805160808101825260039490940290910180546001600160a01b038082168652600160a01b9091046001600160401b03169385018490526001820154168483018190526002909101546060850152905163d6e8f96b60e01b815260048101929092529193508291829163d6e8f96b90602401606060405180830381865afa158015610d19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3d9190612221565b92509250925085846060015110610d545785610d5a565b83606001515b9550818460200151610d6c9190612259565b6001600160401b03164211610ddb57610d878488858961146a565b604080850151815163ffffffff8a168152602081018990526001600160a01b03909116917f1eee30d91b773ac47d7485a3acb6bcd8c7c9cd8d95301b1af361baf5f0991d2e910160405180910390a2610e5e565b610de784888888611737565b600080610df7868686868c61184c565b604080890151815163ffffffff8e16815260208101849052918201849052606082018c90529294509092506001600160a01b03909116907f7d3a26e8d43c5b70f86266bfa26c212e3c097716ff7240ccb6a9034e48754e239060800160405180910390a250505b50505050505050565b60008181548110610e7757600080fd5b60009182526020909120600390910201805460018201546002909201546001600160a01b038083169450600160a01b9092046001600160401b031692919091169084565b60405163aa5dd7f160e01b81526001600160a01b038481166004830152600091859130917f00000000000000000000000000000000000000000000000000000000000000009091169063aa5dd7f190602401602060405180830381865afa158015610f2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4e919061217b565b6001600160a01b031614610f75576040516369229ba760e01b815260040160405180910390fd5b600085905083816001600160a01b031663a035b1fe6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610fbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fdf91906121f2565b14610ffd57604051630d57523160e11b815260040160405180910390fd5b806001600160a01b031663d8dfeb456040518163ffffffff1660e01b81526004016020604051808303816000875af115801561103d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611061919061217b565b6001600160a01b03166323b872dd3330886040518463ffffffff1660e01b8152600401611090939291906121b1565b6020604051808303816000875af11580156110af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d391906121d5565b5060008054604080516080810182523381526001600160401b03428116602083019081526001600160a01b03878116848601818152606086018e8152600189018a559880529451600388027f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563810180549551909616600160a01b026001600160e01b0319909516918416919091179390931790935592517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56482018054919094166001600160a01b0319919091161790925593517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56590910155516342d0204160e01b81529091906342d02041906111f190899060040190815260200190565b600060405180830381600087803b15801561120b57600080fd5b505af115801561121f573d6000803e3d6000fd5b505060408051898152602081018590526001600160a01b03861693503392507fc4b384b2c5ca32c8e77081f4083be594a1ea9ba34f208a9f9a458f70608585f5910160405180910390a39695505050505050565b600061128a8a8a8a8a62093a808b8b8b8b8b610238565b9a9950505050505050505050565b60008060008363ffffffff16815481106112b4576112b461220b565b60009182526020918290206040805160808101825260039390930290910180546001600160a01b03808216808652600160a01b9092046001600160401b03169585019590955260018201549094169183019190915260020154606082015291506113215750600092915050565b600080600083604001516001600160a01b031663d6e8f96b85602001516040518263ffffffff1660e01b815260040161136991906001600160401b0391909116815260200190565b606060405180830381865afa158015611386573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113aa9190612221565b9250925092506113ca8285602001516113c39190612259565b8285611cf2565b9695505050505050565b6001600160a01b038216600081815260016020908152604080832033845290915280822080549290555163a9059cbb60e01b815290919063a9059cbb906114219085908590600401612198565b6020604051808303816000875af1158015611440573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146491906121d5565b50505050565b83602001516001600160401b0316420361148357600080fd5b83516001600160a01b031633146115495783516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906323b872dd903390670de0b6b3a76400006114dc878761215e565b6114e69190612280565b6040518463ffffffff1660e01b8152600401611504939291906121b1565b6020604051808303816000875af1158015611523573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154791906121d5565b505b83604001516001600160a01b03166349746f10826040518263ffffffff1660e01b815260040161157b91815260200190565b600060405180830381600087803b15801561159557600080fd5b505af11580156115a9573d6000803e3d6000fd5b5050505083604001516001600160a01b031663d8dfeb456040518163ffffffff1660e01b81526004016020604051808303816000875af11580156115f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611615919061217b565b6001600160a01b031663a9059cbb33836040518363ffffffff1660e01b8152600401611642929190612198565b6020604051808303816000875af1158015611661573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168591906121d5565b5083606001518110156116d5578084606001516116a291906122a2565b60008463ffffffff16815481106116bb576116bb61220b565b906000526020600020906003020160020181905550611464565b836060015181146116e557600080fd5b60008363ffffffff16815481106116fe576116fe61220b565b60009182526020822060039091020180546001600160e01b03191681556001810180546001600160a01b03191690556002015550505050565b6117ab84604001516001600160a01b031663d8dfeb456040518163ffffffff1660e01b81526004016020604051808303816000875af115801561177e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117a2919061217b565b85518484611d8a565b818460600151036118075760008363ffffffff16815481106117cf576117cf61220b565b60009182526020822060039091020180546001600160e01b03191681556001810180546001600160a01b031916905560020155611464565b8160008463ffffffff16815481106118215761182161220b565b9060005260206000209060030201600201600082825461184191906122a2565b909155505050505050565b6000806000806000808a604001516001600160a01b03166320aa9e7233896040518363ffffffff1660e01b8152600401611887929190612198565b6080604051808303816000875af11580156118a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ca91906122b5565b93509350935093506000670de0b6b3a7640000846118f88c8f602001516118f19190612259565b8c8f611cf2565b611902919061215e565b61190c9190612280565b6040516323b872dd60e01b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906323b872dd9061195f903390309086906004016121b1565b6020604051808303816000875af115801561197e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a291906121d5565b506000620f42406119b5614e208461215e565b6119bf9190612280565b8d5160405163a9059cbb60e01b81529192506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163a9059cbb91611a10918590600401612198565b6020604051808303816000875af1158015611a2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5391906121d5565b506000611a6082846122a2565b905084811115611bc5576000620f4240611a7a87846122a2565b611a8a9063ffffffff881661215e565b611a949190612280565b60405163375edc7760e11b81529091506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636ebdb8ee90611ae59030908590600401612198565b600060405180830381600087803b158015611aff57600080fd5b505af1158015611b13573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb89838986611b5491906122a2565b611b5e91906122a2565b6040518363ffffffff1660e01b8152600401611b7b929190612198565b6020604051808303816000875af1158015611b9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bbe91906121d5565b5050611c57565b84811015611c57576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663d1fa5e9830611c0784896122a2565b6040518363ffffffff1660e01b8152600401611c24929190612198565b600060405180830381600087803b158015611c3e57600080fd5b505af1158015611c52573d6000803e3d6000fd5b505050505b6040516363b20c3360e11b81526004810186905263ffffffff851660248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063c764186690604401600060405180830381600087803b158015611cc557600080fd5b505af1158015611cd9573d6000803e3d6000fd5b50979a5093985050505050505050509550959350505050565b6000426001600160401b0380861690821611611d115782915050611d83565b611d1b8486612259565b6001600160401b0316816001600160401b031610611d3d576000915050611d83565b6000611d498683612301565b611d539086612301565b6001600160401b0316905080856001600160401b031685611d749190612280565b611d7e919061215e565b925050505b9392505050565b8015611e16576001600160a01b03808516600090815260016020908152604080832093871683529290529081208054849290611dc7908490612321565b92505081905550826001600160a01b03167f8ab298b78a235f73eee230f82012c0cf4db76003eaabd16a0195f112e7d625c88584604051611e09929190612198565b60405180910390a2611464565b60405163a9059cbb60e01b81526001600160a01b0385169063a9059cbb90611e449086908690600401612198565b6020604051808303816000875af1158015611e63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e8791906121d5565b5050505050565b6001600160a01b0381168114611ea357600080fd5b50565b6001600160401b0381168114611ea357600080fd5b63ffffffff81168114611ea357600080fd5b6000806000806000806000806000806101408b8d031215611eed57600080fd5b8a35611ef881611e8e565b995060208b0135985060408b0135975060608b0135965060808b0135955060a08b0135945060c08b0135611f2b81611ea6565b935060e08b0135611f3b81611ebb565b92506101008b013591506101208b0135611f5481611ebb565b809150509295989b9194979a5092959850565b60008060008060808587031215611f7d57600080fd5b8435611f8881611e8e565b966020860135965060408601359560600135945092505050565b60008060408385031215611fb557600080fd5b8235611fc081611e8e565b91506020830135611fd081611e8e565b809150509250929050565b8015158114611ea357600080fd5b600080600060608486031215611ffe57600080fd5b833561200981611ebb565b925060208401359150604084013561202081611fdb565b809150509250925092565b60006020828403121561203d57600080fd5b5035919050565b60008060006060848603121561205957600080fd5b833561206481611e8e565b95602085013595506040909401359392505050565b60008060008060008060008060006101208a8c03121561209857600080fd5b89356120a381611e8e565b985060208a0135975060408a0135965060608a0135955060808a0135945060a08a01356120cf81611ea6565b935060c08a01356120df81611ebb565b925060e08a013591506101008a01356120f781611ebb565b809150509295985092959850929598565b60006020828403121561211a57600080fd5b8135611d8381611ebb565b60006020828403121561213757600080fd5b815160ff81168114611d8357600080fd5b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761217557612175612148565b92915050565b60006020828403121561218d57600080fd5b8151611d8381611e8e565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6000602082840312156121e757600080fd5b8151611d8381611fdb565b60006020828403121561220457600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b60008060006060848603121561223657600080fd5b83519250602084015161224881611ea6565b604085015190925061202081611ea6565b6001600160401b0381811683821601908082111561227957612279612148565b5092915050565b60008261229d57634e487b7160e01b600052601260045260246000fd5b500490565b8181038181111561217557612175612148565b600080600080608085870312156122cb57600080fd5b84516122d681611e8e565b80945050602085015192506040850151915060608501516122f681611ebb565b939692955090935050565b6001600160401b0382811682821603908082111561227957612279612148565b808201808211156121755761217561214856fea2646970667358221220d8b8ffc2fffb45af378c6dd602d0b56a09dfe51cd781980d51227bbb938d024f64736f6c63430008140033000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb0000000000000000000000000cde500e6940931ed190ded77bb48640c9486392","name":"MintingHub","is_blueprint":false,"license_type":"none","is_fully_verified":true,"is_verified_via_eth_bytecode_db":true,"language":"solidity","evm_version":"paris","can_be_visualized_via_sol2uml":true,"is_verified_via_sourcify":true,"additional_sources":[{"file_path":"contracts/interface/IERC20.sol","source_code":"/**\r\n * SPDX-License-Identifier: MIT\r\n *\r\n * Copyright (c) 2016-2019 zOS Global Limited\r\n *\r\n */\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @dev Interface of the ERC20 standard as defined in the EIP. Does not include\r\n * the optional functions; to access them see `ERC20Detailed`.\r\n */\r\n\r\ninterface IERC20 {\r\n    function name() external view returns (string memory);\r\n\r\n    function symbol() external view returns (string memory);\r\n\r\n    function decimals() external view returns (uint8);\r\n\r\n    /**\r\n     * @dev Returns the amount of tokens in existence.\r\n     */\r\n    function totalSupply() external view returns (uint256);\r\n\r\n    /**\r\n     * @dev Returns the amount of tokens owned by `account`.\r\n     */\r\n    function balanceOf(address account) external view returns (uint256);\r\n\r\n    /**\r\n     * @dev Moves `amount` tokens from the caller's account to `recipient`.\r\n     *\r\n     * Returns always true. Throws error on failure.\r\n     *\r\n     * Emits a `Transfer` event.\r\n     */\r\n    function transfer(address recipient, uint256 amount) external returns (bool);\r\n\r\n    /**\r\n     * @dev Returns the remaining number of tokens that `spender` will be\r\n     * allowed to spend on behalf of `owner` through `transferFrom`. This is\r\n     * zero by default.\r\n     *\r\n     * This value can change when `approve` or `transferFrom` are called.\r\n     */\r\n    function allowance(address owner, address spender) external view returns (uint256);\r\n\r\n    /**\r\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\r\n     *\r\n     * Returns a boolean value indicating whether the operation succeeded.\r\n     *\r\n     * > Beware that changing an allowance with this method brings the risk\r\n     * that someone may use both the old and the new allowance by unfortunate\r\n     * transaction ordering. One possible solution to mitigate this race\r\n     * condition is to first reduce the spender's allowance to 0 and set the\r\n     * desired value afterwards:\r\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\r\n     *\r\n     * Emits an `Approval` event.\r\n     */\r\n    function approve(address spender, uint256 amount) external returns (bool);\r\n\r\n    /**\r\n     * @dev Moves `amount` tokens from `sender` to `recipient` using the\r\n     * allowance mechanism. `amount` is then deducted from the caller's\r\n     * allowance.\r\n     *\r\n     * Returns always true. Throws error on failure.\r\n     *\r\n     * Emits a `Transfer` event.\r\n     */\r\n    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\r\n\r\n    /**\r\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\r\n     * another (`to`).\r\n     *\r\n     * Note that `value` may be zero.\r\n     */\r\n    event Transfer(address indexed from, address indexed to, uint256 value);\r\n\r\n    /**\r\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\r\n     * a call to `approve`. `value` is the new allowance.\r\n     */\r\n    event Approval(address indexed owner, address indexed spender, uint256 value);\r\n}\r\n"},{"file_path":"contracts/interface/IFrankencoin.sol","source_code":"// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.0;\r\n\r\nimport \"./IERC20.sol\";\r\nimport \"./IReserve.sol\";\r\n\r\ninterface IFrankencoin is IERC20 {\r\n    function suggestMinter(address _minter, uint256 _applicationPeriod, uint256 _applicationFee, string calldata _message) external;\r\n\r\n    function registerPosition(address position) external;\r\n\r\n    function denyMinter(address minter, address[] calldata helpers, string calldata message) external;\r\n\r\n    function reserve() external view returns (IReserve);\r\n\r\n    function minterReserve() external view returns (uint256);\r\n\r\n    function calculateAssignedReserve(uint256 mintedAmount, uint32 _reservePPM) external view returns (uint256);\r\n\r\n    function equity() external view returns (uint256);\r\n\r\n    function isMinter(address minter) external view returns (bool);\r\n\r\n    function getPositionParent(address position) external view returns (address);\r\n\r\n    function mint(address target, uint256 amount) external;\r\n\r\n    function mintWithReserve(address target, uint256 amount, uint32 reservePPM, uint32 feePPM) external;\r\n\r\n    function burnFrom(address target, uint256 amount) external;\r\n\r\n    function burnWithoutReserve(uint256 amountIncludingReserve, uint32 reservePPM) external;\r\n\r\n    function burnFromWithReserve(address payer, uint256 targetTotalBurnAmount, uint32 _reservePPM) external returns (uint256);\r\n\r\n    function burnWithReserve(uint256 amountExcludingReserve, uint32 reservePPM) external returns (uint256);\r\n\r\n    function coverLoss(address source, uint256 amount) external;\r\n\r\n    function collectProfits(address source, uint256 _amount) external;\r\n}\r\n"},{"file_path":"contracts/interface/IPosition.sol","source_code":"// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.0;\r\n\r\nimport \"./IERC20.sol\";\r\nimport \"./IReserve.sol\";\r\nimport \"./IFrankencoin.sol\";\r\n\r\ninterface IPosition {\r\n\r\n    function original() external returns (address);\r\n\r\n    function collateral() external returns (IERC20);\r\n\r\n    function minimumCollateral() external returns (uint256);\r\n\r\n    function challengePeriod() external returns (uint64);\r\n\r\n    function expiration() external returns (uint256);\r\n\r\n    function price() external returns (uint256);\r\n\r\n    function reduceLimitForClone(uint256 amount) external;\r\n\r\n    function initializeClone(address owner, uint256 _price, uint256 _coll, uint256 _mint, uint256 expiration) external;\r\n\r\n    function deny(address[] calldata helpers, string calldata message) external;\r\n\r\n    function mint(address target, uint256 amount) external;\r\n\r\n    function minted() external returns (uint256);\r\n\r\n    function reserveContribution() external returns (uint32);\r\n\r\n    function getUsableMint(uint256 totalMint, bool beforeFees) external view returns (uint256);\r\n\r\n    function challengeData(uint256 challengeStart) external view returns (uint256 liqPrice, uint64 phase1, uint64 phase2);\r\n\r\n    function notifyChallengeStarted(uint256 size) external;\r\n\r\n    function notifyChallengeAverted(uint256 size) external;\r\n\r\n    function notifyChallengeSucceeded(address _bidder, uint256 _size) external returns (address, uint256, uint256, uint32);\r\n\r\n}"},{"file_path":"contracts/interface/IPositionFactory.sol","source_code":"// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.0;\r\n\r\ninterface IPositionFactory {\r\n    function createNewPosition(\r\n        address _owner,\r\n        address _zchf,\r\n        address _collateral,\r\n        uint256 _minCollateral,\r\n        uint256 _initialLimit,\r\n        uint256 _initPeriodSeconds,\r\n        uint256 _duration,\r\n        uint64 _challengePeriod,\r\n        uint32 _annualInterestPPM,\r\n        uint256 _liqPrice,\r\n        uint32 _reserve\r\n    ) external returns (address);\r\n\r\n    function clonePosition(address _existing) external returns (address);\r\n}\r\n"},{"file_path":"contracts/interface/IReserve.sol","source_code":"// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.0;\r\n\r\nimport \"./IERC20.sol\";\r\n\r\ninterface IReserve is IERC20 {\r\n   function invest(uint256 amount, uint256 expected) external returns (uint256);\r\n   function checkQualified(address sender, address[] calldata helpers) external view;\r\n}"}],"certified":false,"conflicting_implementations":null,"abi":[{"inputs":[{"internalType":"address","name":"_zchf","type":"address"},{"internalType":"address","name":"_factory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidPos","type":"error"},{"inputs":[],"name":"UnexpectedPrice","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"position","type":"address"},{"indexed":false,"internalType":"uint256","name":"number","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"size","type":"uint256"}],"name":"ChallengeAverted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"challenger","type":"address"},{"indexed":true,"internalType":"address","name":"position","type":"address"},{"indexed":false,"internalType":"uint256","name":"size","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"number","type":"uint256"}],"name":"ChallengeStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"position","type":"address"},{"indexed":false,"internalType":"uint256","name":"number","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"acquiredCollateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"challengeSize","type":"uint256"}],"name":"ChallengeSucceeded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"position","type":"address"},{"indexed":false,"internalType":"address","name":"zchf","type":"address"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"PositionOpened","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"collateral","type":"address"},{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PostPonedReturn","type":"event"},{"inputs":[],"name":"CHALLENGER_REWARD","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPENING_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_challengeNumber","type":"uint32"},{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"bool","name":"postponeCollateralReturn","type":"bool"}],"name":"bid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_positionAddr","type":"address"},{"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"internalType":"uint256","name":"expectedPrice","type":"uint256"}],"name":"challenge","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"challenges","outputs":[{"internalType":"address","name":"challenger","type":"address"},{"internalType":"uint64","name":"start","type":"uint64"},{"internalType":"contract IPosition","name":"position","type":"address"},{"internalType":"uint256","name":"size","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"position","type":"address"},{"internalType":"uint256","name":"_initialCollateral","type":"uint256"},{"internalType":"uint256","name":"_initialMint","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"}],"name":"clone","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_collateralAddress","type":"address"},{"internalType":"uint256","name":"_minCollateral","type":"uint256"},{"internalType":"uint256","name":"_initialCollateral","type":"uint256"},{"internalType":"uint256","name":"_mintingMaximum","type":"uint256"},{"internalType":"uint256","name":"_initPeriodSeconds","type":"uint256"},{"internalType":"uint256","name":"_expirationSeconds","type":"uint256"},{"internalType":"uint64","name":"_challengeSeconds","type":"uint64"},{"internalType":"uint32","name":"_annualInterestPPM","type":"uint32"},{"internalType":"uint256","name":"_liqPrice","type":"uint256"},{"internalType":"uint32","name":"_reservePPM","type":"uint32"}],"name":"openPosition","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_collateralAddress","type":"address"},{"internalType":"uint256","name":"_minCollateral","type":"uint256"},{"internalType":"uint256","name":"_initialCollateral","type":"uint256"},{"internalType":"uint256","name":"_mintingMaximum","type":"uint256"},{"internalType":"uint256","name":"_expirationSeconds","type":"uint256"},{"internalType":"uint64","name":"_challengeSeconds","type":"uint64"},{"internalType":"uint32","name":"_annualInterestPPM","type":"uint32"},{"internalType":"uint256","name":"_liqPrice","type":"uint256"},{"internalType":"uint32","name":"_reservePPM","type":"uint32"}],"name":"openPositionOneWeek","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collateral","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"pendingReturns","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"challengeNumber","type":"uint32"}],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collateral","type":"address"},{"internalType":"address","name":"target","type":"address"}],"name":"returnPostponedCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"zchf","outputs":[{"internalType":"contract IFrankencoin","name":"","type":"address"}],"stateMutability":"view","type":"function"}],"is_changed_bytecode":false,"is_partially_verified":false,"constructor_args":"000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb0000000000000000000000000cde500e6940931ed190ded77bb48640c9486392"}