creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/interface/IERC20.sol": {
        "content": "/**\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"
      },
      "contracts/interface/IFrankencoin.sol": {
        "content": "// 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"
      },
      "contracts/interface/IPosition.sol": {
        "content": "// 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}"
      },
      "contracts/interface/IReserve.sol": {
        "content": "// 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}"
      },
      "contracts/Position.sol": {
        "content": "// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.0;\r\n\r\nimport \"./utils/Ownable.sol\";\r\nimport \"./utils/MathUtil.sol\";\r\n\r\nimport \"./interface/IERC20.sol\";\r\nimport \"./interface/IPosition.sol\";\r\nimport \"./interface/IReserve.sol\";\r\nimport \"./interface/IFrankencoin.sol\";\r\n\r\n/**\r\n * @title Position\r\n * @notice A collateralized minting position.\r\n */\r\ncontract Position is Ownable, IPosition, MathUtil {\r\n    /**\r\n     * @notice Note that this contract is intended to be cloned. All clones will share the same values for\r\n     * the constant and immutable fields, but have their own values for the other fields.\r\n     */\r\n\r\n    /**\r\n     * @notice The zchf price per unit of the collateral below which challenges succeed, (36 - collateral.decimals) decimals\r\n     */\r\n    uint256 public price;\r\n\r\n    /**\r\n     * @notice Net minted amount, including reserve.\r\n     */\r\n    uint256 public minted;\r\n\r\n    /**\r\n     * @notice Amount of the collateral that is currently under a challenge.\r\n     * Used to figure out whether there are pending challenges.\r\n     */\r\n    uint256 public challengedAmount;\r\n\r\n    /**\r\n     * @notice Challenge period in seconds.\r\n     */\r\n    uint64 public immutable challengePeriod;\r\n\r\n    /**\r\n     * @notice End of the latest cooldown. If this is in the future, minting is suspended.\r\n     */\r\n    uint256 public cooldown;\r\n\r\n    /**\r\n     * @notice How much can be minted at most.\r\n     */\r\n    uint256 public limit;\r\n\r\n    /**\r\n     * @notice Timestamp when minting can start and the position no longer denied.\r\n     */\r\n    uint256 public immutable start;\r\n\r\n    /**\r\n     * @notice Timestamp of the expiration of the position. After expiration, challenges cannot be averted\r\n     * any more. This is also the basis for fee calculations.\r\n     */\r\n    uint256 public expiration;\r\n\r\n    /**\r\n     * @notice The original position to help identifying clones.\r\n     */\r\n    address public immutable original;\r\n\r\n    /**\r\n     * @notice Pointer to the minting hub.\r\n     */\r\n    address public immutable hub;\r\n\r\n    /**\r\n     * @notice The Frankencoin contract.\r\n     */\r\n    IFrankencoin public immutable zchf;\r\n\r\n    /**\r\n     * @notice The collateral token.\r\n     */\r\n    IERC20 public immutable override collateral;\r\n\r\n    /**\r\n     * @notice Minimum acceptable collateral amount to prevent dust.\r\n     */\r\n    uint256 public immutable override minimumCollateral;\r\n\r\n    /**\r\n     * @notice Always pay interest for at least four weeks.\r\n     */\r\n    uint256 private constant MIN_INTEREST_DURATION = 4 weeks;\r\n\r\n    /**\r\n     * @notice The interest in parts per million per year that is deducted when minting Frankencoins.\r\n     * To be paid upfront.\r\n     */\r\n    uint32 public immutable annualInterestPPM;\r\n\r\n    /**\r\n     * @notice The reserve contribution in parts per million of the minted amount.\r\n     */\r\n    uint32 public immutable reserveContribution;\r\n\r\n    event MintingUpdate(uint256 collateral, uint256 price, uint256 minted, uint256 limit);\r\n    event PositionDenied(address indexed sender, string message); // emitted if closed by governance\r\n\r\n    error InsufficientCollateral();\r\n    error TooLate();\r\n    error RepaidTooMuch(uint256 excess);\r\n    error LimitExceeded();\r\n    error ChallengeTooSmall();\r\n    error Expired();\r\n    error Hot();\r\n    error Challenged();\r\n    error NotHub();\r\n\r\n    modifier alive() {\r\n        if (block.timestamp >= expiration) revert Expired();\r\n        _;\r\n    }\r\n\r\n    modifier noCooldown() {\r\n        if (block.timestamp <= cooldown) revert Hot();\r\n        _;\r\n    }\r\n\r\n    modifier noChallenge() {\r\n        if (challengedAmount > 0) revert Challenged();\r\n        _;\r\n    }\r\n\r\n    modifier onlyHub() {\r\n        if (msg.sender != address(hub)) revert NotHub();\r\n        _;\r\n    }\r\n\r\n    /**\r\n     * @dev See MintingHub.openPosition\r\n     */\r\n    constructor(\r\n        address _owner,\r\n        address _hub,\r\n        address _zchf,\r\n        address _collateral,\r\n        uint256 _minCollateral,\r\n        uint256 _initialLimit,\r\n        uint256 _initPeriod,\r\n        uint256 _duration,\r\n        uint64 _challengePeriod,\r\n        uint32 _annualInterestPPM,\r\n        uint256 _liqPrice,\r\n        uint32 _reservePPM\r\n    ) {\r\n        require(_initPeriod >= 3 days); // must be at least three days, recommended to use higher values\r\n        _setOwner(_owner);\r\n        original = address(this);\r\n        hub = _hub;\r\n        zchf = IFrankencoin(_zchf);\r\n        collateral = IERC20(_collateral);\r\n        annualInterestPPM = _annualInterestPPM;\r\n        reserveContribution = _reservePPM;\r\n        minimumCollateral = _minCollateral;\r\n        challengePeriod = _challengePeriod;\r\n        start = block.timestamp + _initPeriod; // at least three days time to deny the position\r\n        cooldown = start;\r\n        expiration = start + _duration;\r\n        limit = _initialLimit;\r\n        _setPrice(_liqPrice);\r\n    }\r\n\r\n    /**\r\n     * @notice Method to initialize a freshly created clone. It is the responsibility of the creator to make sure this is only\r\n     * called once and to call reduceLimitForClone on the original position before initializing the clone.\r\n     */\r\n    function initializeClone(\r\n        address owner,\r\n        uint256 _price,\r\n        uint256 _coll,\r\n        uint256 _initialMint,\r\n        uint256 expirationTime\r\n    ) external onlyHub {\r\n        if (_coll < minimumCollateral) revert InsufficientCollateral();\r\n        uint256 impliedPrice = (_initialMint * ONE_DEC18) / _coll;\r\n        _initialMint = (impliedPrice * _coll) / ONE_DEC18; // to cancel potential rounding errors\r\n        if (impliedPrice > _price) revert InsufficientCollateral();\r\n        _setOwner(owner);\r\n        limit = _initialMint;\r\n        expiration = expirationTime;\r\n        _setPrice(impliedPrice);\r\n        _mint(owner, _initialMint, _coll);\r\n    }\r\n\r\n    function limitForClones() public view returns (uint256) {\r\n        uint256 backedLimit = (_collateralBalance() * price) / ONE_DEC18;\r\n        if (backedLimit >= limit) {\r\n            return 0;\r\n        } else {\r\n            // due to invariants, this is always below (limit - minted)\r\n            return limit - backedLimit;\r\n        }\r\n    }\r\n\r\n    /**\r\n     * @notice Adjust this position's limit to allow a clone to mint its own Frankencoins.\r\n     * Invariant: global limit stays the same.\r\n     *\r\n     * Cloning a position is only allowed if the position is not challenged, not expired and not in cooldown.\r\n     */\r\n    function reduceLimitForClone(uint256 mint_) external noChallenge noCooldown alive onlyHub {\r\n        if (mint_ > limitForClones()) revert LimitExceeded();\r\n        limit -= mint_;\r\n    }\r\n\r\n    /**\r\n     * @notice Qualified pool share holders can call this method to immediately expire a freshly proposed position.\r\n     */\r\n    function deny(address[] calldata helpers, string calldata message) external {\r\n        if (block.timestamp >= start) revert TooLate();\r\n        IReserve(zchf.reserve()).checkQualified(msg.sender, helpers);\r\n        _close(); // since expiration is immutable, we put it under eternal cooldown\r\n        emit PositionDenied(msg.sender, message);\r\n    }\r\n\r\n    function _close() internal {\r\n        cooldown = type(uint256).max;\r\n    }\r\n\r\n    function isClosed() public view returns (bool) {\r\n        return cooldown == type(uint256).max;\r\n    }\r\n\r\n    /**\r\n     * @notice This is how much the minter can actually use when minting ZCHF, with the rest being used\r\n     * assigned to the minter reserve or (if applicable) fees.\r\n     */\r\n    function getUsableMint(uint256 totalMint, bool afterFees) external view returns (uint256) {\r\n        if (afterFees) {\r\n            return (totalMint * (1000_000 - reserveContribution - calculateCurrentFee())) / 1000_000;\r\n        } else {\r\n            return (totalMint * (1000_000 - reserveContribution)) / 1000_000;\r\n        }\r\n    }\r\n\r\n    /**\r\n     * @notice \"All in one\" function to adjust the outstanding amount of ZCHF, the collateral amount,\r\n     * and the price in one transaction.\r\n     */\r\n    function adjust(uint256 newMinted, uint256 newCollateral, uint256 newPrice) external onlyOwner {\r\n        uint256 colbal = _collateralBalance();\r\n        if (newCollateral > colbal) {\r\n            collateral.transferFrom(msg.sender, address(this), newCollateral - colbal);\r\n        }\r\n        // Must be called after collateral deposit, but before withdrawal\r\n        if (newMinted < minted) {\r\n            zchf.burnFromWithReserve(msg.sender, minted - newMinted, reserveContribution);\r\n            minted = newMinted;\r\n        }\r\n        if (newCollateral < colbal) {\r\n            withdrawCollateral(msg.sender, colbal - newCollateral);\r\n        }\r\n        // Must be called after collateral withdrawal\r\n        if (newMinted > minted) {\r\n            mint(msg.sender, newMinted - minted);\r\n        }\r\n        if (newPrice != price) {\r\n            adjustPrice(newPrice);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * @notice Allows the position owner to adjust the liquidation price as long as there is no pending challenge.\r\n     * Lowering the liquidation price can be done with immediate effect, given that there is enough collateral.\r\n     * Increasing the liquidation price triggers a cooldown period of 3 days, during which minting is suspended.\r\n     */\r\n    function adjustPrice(uint256 newPrice) public onlyOwner noChallenge {\r\n        if (newPrice > price) {\r\n            _restrictMinting(3 days);\r\n        } else {\r\n            _checkCollateral(_collateralBalance(), newPrice);\r\n        }\r\n        _setPrice(newPrice);\r\n        emit MintingUpdate(_collateralBalance(), price, minted, limit);\r\n    }\r\n\r\n    function _setPrice(uint256 newPrice) internal {\r\n        require(newPrice * minimumCollateral <= limit * ONE_DEC18); // sanity check\r\n        price = newPrice;\r\n    }\r\n\r\n    function _collateralBalance() internal view returns (uint256) {\r\n        return IERC20(collateral).balanceOf(address(this));\r\n    }\r\n\r\n    /**\r\n     * @notice Mint ZCHF as long as there is no open challenge, the position is not subject to a cooldown,\r\n     * and there is sufficient collateral.\r\n     */\r\n    function mint(address target, uint256 amount) public onlyOwner noChallenge noCooldown alive {\r\n        _mint(target, amount, _collateralBalance());\r\n    }\r\n\r\n    function calculateCurrentFee() public view returns (uint32) {\r\n        uint256 exp = expiration;\r\n        uint256 time = block.timestamp < start ? start : block.timestamp;\r\n        uint256 timePassed = time >= exp - MIN_INTEREST_DURATION ? MIN_INTEREST_DURATION : exp - time;\r\n        // Time resolution is in the range of minutes for typical interest rates.\r\n        return uint32((timePassed * annualInterestPPM) / 365 days);\r\n    }\r\n\r\n    function _mint(address target, uint256 amount, uint256 collateral_) internal {\r\n        if (minted + amount > limit) revert LimitExceeded();\r\n        zchf.mintWithReserve(target, amount, reserveContribution, calculateCurrentFee());\r\n        minted += amount;\r\n\r\n        _checkCollateral(collateral_, price);\r\n        emit MintingUpdate(_collateralBalance(), price, minted, limit);\r\n    }\r\n\r\n    function _restrictMinting(uint256 period) internal {\r\n        uint256 horizon = block.timestamp + period;\r\n        if (horizon > cooldown) {\r\n            cooldown = horizon;\r\n        }\r\n    }\r\n\r\n    /**\r\n     * @notice Repay some ZCHF. If too much is repaid, the call fails.\r\n     * It is possible to repay while there are challenges, but the collateral is locked until all is clear again.\r\n     *\r\n     * The repaid amount should fulfill the following equation in order to close the position,\r\n     * i.e. bring the minted amount to 0:\r\n     * minted = amount + zchf.calculateAssignedReserve(amount, reservePPM)\r\n     *\r\n     * Under normal circumstances, this implies:\r\n     * amount = minted * (1000000 - reservePPM)\r\n     *\r\n     * E.g. if minted is 50 and reservePPM is 200000, it is necessary to repay 40 to be able to close the position.\r\n     */\r\n    function repay(uint256 amount) public {\r\n        IERC20(zchf).transferFrom(msg.sender, address(this), amount);\r\n        uint256 actuallyRepaid = IFrankencoin(zchf).burnWithReserve(amount, reserveContribution);\r\n        _notifyRepaid(actuallyRepaid);\r\n        emit MintingUpdate(_collateralBalance(), price, minted, limit);\r\n    }\r\n\r\n    function _notifyRepaid(uint256 amount) internal {\r\n        if (amount > minted) revert RepaidTooMuch(amount - minted);\r\n        minted -= amount;\r\n    }\r\n\r\n    /**\r\n     * @notice Withdraw any ERC20 token that might have ended up on this address.\r\n     * Withdrawing collateral is subject to the same restrictions as withdrawCollateral(...).\r\n     */\r\n    function withdraw(address token, address target, uint256 amount) external onlyOwner {\r\n        if (token == address(collateral)) {\r\n            withdrawCollateral(target, amount);\r\n        } else {\r\n            uint256 balance = _collateralBalance();\r\n            IERC20(token).transfer(target, amount);\r\n            require(balance == _collateralBalance()); // guard against double-entry-point tokens\r\n        }\r\n    }\r\n\r\n    /**\r\n     * @notice Withdraw collateral from the position up to the extent that it is still well collateralized afterwards.\r\n     * Not possible as long as there is an open challenge or the contract is subject to a cooldown.\r\n     *\r\n     * Withdrawing collateral below the minimum collateral amount formally closes the position.\r\n     */\r\n    function withdrawCollateral(address target, uint256 amount) public onlyOwner noChallenge {\r\n        if (block.timestamp <= cooldown && !isClosed()) revert Hot();\r\n        uint256 balance = _withdrawCollateral(target, amount);\r\n        _checkCollateral(balance, price);\r\n        if (balance < minimumCollateral && balance > 0) revert InsufficientCollateral(); // Prevent dust amounts\r\n    }\r\n\r\n    function _withdrawCollateral(address target, uint256 amount) internal returns (uint256) {\r\n        if (amount > 0) {\r\n            // Some weird tokens fail when trying to transfer 0 amounts\r\n            IERC20(collateral).transfer(target, amount);\r\n        }\r\n        uint256 balance = _collateralBalance();\r\n        _considerClose(balance);\r\n        emit MintingUpdate(balance, price, minted, limit);\r\n        return balance;\r\n    }\r\n\r\n    function _considerClose(uint256 collateralBalance) internal {\r\n        if (collateralBalance < minimumCollateral && challengedAmount == 0) {\r\n            // This leaves a slightly unsatisfying possibility open: if the withdrawal happens due to a successful\r\n            // challenge, there might be a small amount of collateral left that is not withheld in case there are no\r\n            // other pending challenges. The only way to cleanly solve this would be to have two distinct cooldowns,\r\n            // one for minting and one for withdrawals.\r\n            _close();\r\n        }\r\n    }\r\n\r\n    /**\r\n     * @notice This invariant must always hold and must always be checked when any of the three\r\n     * variables change in an adverse way.\r\n     */\r\n    function _checkCollateral(uint256 collateralReserve, uint256 atPrice) internal view {\r\n        if (collateralReserve * atPrice < minted * ONE_DEC18) revert InsufficientCollateral();\r\n    }\r\n\r\n    /**\r\n     * @notice Returns the liquidation price and the durations for phase1 and phase2 of the challenge.\r\n     * Both phases are usually of equal duration, but near expiration, phase one is adjusted such that\r\n     * it cannot last beyond the expiration date of the position.\r\n     */\r\n    function challengeData(uint256 challengeStart) external view returns (uint256 liqPrice, uint64 phase1, uint64 phase2) {\r\n        uint256 timeToExpiration = challengeStart >= expiration ? 0 : expiration - challengeStart;\r\n        return (price, uint64(_min(timeToExpiration, challengePeriod)), challengePeriod);\r\n    }\r\n\r\n    function notifyChallengeStarted(uint256 size) external onlyHub {\r\n        // Require minimum size. Collateral balance can be below minimum if it was partially challenged before.\r\n        if (size < minimumCollateral && size < _collateralBalance()) revert ChallengeTooSmall();\r\n        if (size == 0) revert ChallengeTooSmall();\r\n        challengedAmount += size;\r\n    }\r\n\r\n    /**\r\n     * @param size   amount of collateral challenged (dec18)\r\n     */\r\n    function notifyChallengeAverted(uint256 size) external onlyHub {\r\n        challengedAmount -= size;\r\n\r\n        // Don't allow minter to close the position immediately so challenge can be repeated before\r\n        // the owner has a chance to mint more on an undercollateralized position\r\n        _restrictMinting(1 days);\r\n\r\n        // If this was the last open challenge and there is only a dust amount of collateral left, the position should be closed\r\n        _considerClose(_collateralBalance());\r\n    }\r\n\r\n    /**\r\n     * @notice Notifies the position that a challenge was successful.\r\n     * Triggers the payout of the challenged part of the collateral.\r\n     * Everything else is assumed to be handled by the hub.\r\n     *\r\n     * @param _bidder   address of the bidder that receives the collateral\r\n     * @param _size     amount of the collateral bid for\r\n     * @return (position owner, effective challenge size in ZCHF, amount to be repaid, reserve ppm)\r\n     */\r\n    function notifyChallengeSucceeded(\r\n        address _bidder,\r\n        uint256 _size\r\n    ) external onlyHub returns (address, uint256, uint256, uint32) {\r\n        challengedAmount -= _size;\r\n        uint256 colBal = _collateralBalance();\r\n        if (colBal < _size) {\r\n            _size = colBal;\r\n        }\r\n        uint256 repayment = colBal == 0 ? 0 : minted * _size / colBal; // for enormous colBal, this could be rounded to 0, which is ok\r\n        _notifyRepaid(repayment); // we assume the caller takes care of the actual repayment\r\n\r\n        // Give time for additional challenges before the owner can mint again. In particular,\r\n        // the owner might have added collateral only seconds before the challenge ended, preventing a close.\r\n        _restrictMinting(3 days);\r\n        \r\n        _withdrawCollateral(_bidder, _size); // transfer collateral to the bidder and emit update\r\n\r\n        return (owner, _size, repayment, reserveContribution);\r\n    }\r\n}\r\n"
      },
      "contracts/utils/MathUtil.sol": {
        "content": "// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @title Functions for share valuation\r\n */\r\ncontract MathUtil {\r\n    uint256 internal constant ONE_DEC18 = 10 ** 18;\r\n\r\n    // Let's go for 12 digits of precision (18-6)\r\n    uint256 internal constant THRESH_DEC18 = 10 ** 6;\r\n\r\n    /**\r\n     * @notice Cubic root with Halley approximation\r\n     *         Number 1e18 decimal\r\n     * @param _v     number for which we calculate x**(1/3)\r\n     * @return returns _v**(1/3)\r\n     */\r\n    function _cubicRoot(uint256 _v) internal pure returns (uint256) {\r\n        // Good first guess for _v slightly above 1.0, which is often the case in the Frankencoin system\r\n        uint256 x = _v > ONE_DEC18 && _v < 10 ** 19 ? (_v - ONE_DEC18) / 3 + ONE_DEC18 : ONE_DEC18;\r\n        uint256 diff;\r\n        do {\r\n            uint256 powX3 = _mulD18(_mulD18(x, x), x);\r\n            uint256 xnew = x * (powX3 + 2 * _v) / (2 * powX3 + _v);\r\n            diff = xnew > x ? xnew - x : x - xnew;\r\n            x = xnew;\r\n        } while (diff > THRESH_DEC18);\r\n        return x;\r\n    }\r\n\r\n    function _mulD18(uint256 _a, uint256 _b) internal pure returns (uint256) {\r\n        return (_a * _b) / ONE_DEC18;\r\n    }\r\n\r\n    function _divD18(uint256 _a, uint256 _b) internal pure returns (uint256) {\r\n        return (_a * ONE_DEC18) / _b;\r\n    }\r\n\r\n    function _power3(uint256 _x) internal pure returns (uint256) {\r\n        return _mulD18(_mulD18(_x, _x), _x);\r\n    }\r\n\r\n    function _min(uint256 a, uint256 b) internal pure returns (uint256) {\r\n        return a < b ? a : b;\r\n    }\r\n}\r\n"
      },
      "contracts/utils/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\r\n//\r\n// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol\r\n//\r\n// Modifications:\r\n// - Replaced Context._msgSender() with msg.sender\r\n// - Made leaner\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @dev Contract module which provides a basic access control mechanism, where\r\n * there is an account (an owner) that can be granted exclusive access to\r\n * specific functions.\r\n *\r\n * By default, the owner account will be the one that deploys the contract. This\r\n * can later be changed with {transferOwnership}.\r\n */\r\ncontract Ownable {\r\n    address public owner;\r\n\r\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\r\n\r\n    error NotOwner();\r\n\r\n    /**\r\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\r\n     * Can only be called by the current owner.\r\n     */\r\n    function transferOwnership(address newOwner) public onlyOwner {\r\n        _setOwner(newOwner);\r\n    }\r\n\r\n    /**\r\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\r\n     * Internal function without access restriction.\r\n     */\r\n    function _setOwner(address newOwner) internal {\r\n        require(newOwner != address(0x0));\r\n        address oldOwner = owner;\r\n        owner = newOwner;\r\n        emit OwnershipTransferred(oldOwner, newOwner);\r\n    }\r\n\r\n    function _requireOwner(address sender) internal view {\r\n        if (owner != sender) revert NotOwner();\r\n    }\r\n\r\n    modifier onlyOwner() {\r\n        _requireOwner(msg.sender);\r\n        _;\r\n    }\r\n}\r\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "paris",
      "metadata": {
        "useLiteralContent": true
      },
      "libraries": {}
    }
  }}