{"file_path":"contracts/MintingHubV2/MintingHub.sol","creation_status":"success","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport '../utils/Ownable.sol';\n\nimport '../interface/IERC20.sol';\nimport '../interface/IReserve.sol';\nimport '../interface/ILeadrate.sol';\nimport '../interface/IFrankencoin.sol';\n\nimport './interfaceV2/IPosition.sol';\nimport './interfaceV2/IPositionFactory.sol';\nimport './PositionRoller.sol';\n\n/**\n * @title Minting Hub\n * @notice The central hub for creating, cloning and challenging collateralized Frankencoin positions.\n * @dev Only one instance of this contract is required, whereas every new position comes with a new position\n * contract. Pending challenges are stored as structs in an array.\n */\ncontract MintingHub {\n\t/**\n\t * @notice Irrevocable fee in ZCHF when proposing a new position (but not when cloning an existing one).\n\t */\n\tuint256 public constant OPENING_FEE = 1000 * 10 ** 18;\n\n\t/**\n\t * @notice The challenger reward in parts per million (ppm) relative to the challenged amount, whereas\n\t * challenged amount if defined as the challenged collateral amount times the liquidation price.\n\t */\n\tuint256 public constant CHALLENGER_REWARD = 20000; // 2%\n\tuint256 public constant EXPIRED_PRICE_FACTOR = 10;\n\n\tIPositionFactory private immutable POSITION_FACTORY; // position contract to clone\n\n\tIFrankencoin public immutable zchf; // currency\n\tPositionRoller public immutable roller; // helper to roll positions\n\tILeadrate public immutable rate; // to determine the interest rate\n\n\tChallenge[] public challenges; // list of open challenges\n\n\t/**\n\t * @notice Map to remember pending postponed collateral returns.\n\t * @dev It maps collateral => beneficiary => amount.\n\t */\n\tmapping(address collateral => mapping(address owner => uint256 amount)) public pendingReturns;\n\n\tstruct Challenge {\n\t\taddress challenger; // the address from which the challenge was initiated\n\t\tuint40 start; // the start of the challenge\n\t\tIPosition position; // the position that was challenged\n\t\tuint256 size; // how much collateral the challenger provided\n\t}\n\n\tevent PositionOpened(address indexed owner, address indexed position, address original, address collateral);\n\tevent ChallengeStarted(address indexed challenger, address indexed position, uint256 size, uint256 number);\n\tevent ChallengeAverted(address indexed position, uint256 number, uint256 size);\n\tevent ChallengeSucceeded(address indexed position, uint256 number, uint256 bid, uint256 acquiredCollateral, uint256 challengeSize);\n\tevent PostPonedReturn(address collateral, address indexed beneficiary, uint256 amount);\n\tevent ForcedSale(address pos, uint256 amount, uint256 priceE36MinusDecimals);\n\n\terror UnexpectedPrice();\n\terror InvalidPos();\n\terror IncompatibleCollateral();\n\terror InsufficientCollateral();\n\n\tmodifier validPos(address position) {\n\t\tif (zchf.getPositionParent(position) != address(this)) revert InvalidPos();\n\t\t_;\n\t}\n\n\tconstructor(address _zchf, address _leadrate, address _roller, address _factory) {\n\t\tzchf = IFrankencoin(_zchf);\n\t\trate = ILeadrate(_leadrate);\n\t\tPOSITION_FACTORY = IPositionFactory(_factory);\n\t\troller = PositionRoller(_roller);\n\t}\n\n\t/**\n\t * @notice Open a collateralized loan position. See also https://docs.frankencoin.com/positions/open .\n\t * @dev For a successful call, you must set an allowance for the collateral token, allowing\n\t * the minting hub to transfer the initial collateral amount to the newly created position and to\n\t * withdraw the fees.\n\t *\n\t * @param _collateralAddress        address of collateral token\n\t * @param _minCollateral     minimum collateral required to prevent dust amounts\n\t * @param _initialCollateral amount of initial collateral to be deposited\n\t * @param _mintingMaximum    maximal amount of ZCHF that can be minted by the position owner\n\t * @param _expirationSeconds position tenor in unit of timestamp (seconds) from 'now'\n\t * @param _challengeSeconds  challenge period. Longer for less liquid collateral.\n\t * @param _riskPremium       ppm of minted amount that is added to the applicible minting fee as a risk premium\n\t * @param _liqPrice          Liquidation price with (36 - token decimals) decimals,\n\t *                           e.g. 18 decimals for an 18 dec collateral, 36 decs for a 0 dec collateral.\n\t * @param _reservePPM        ppm of minted amount that is locked as borrower's reserve, e.g. 20%\n\t * @return address           address of created position\n\t */\n\tfunction openPosition(\n\t\taddress _collateralAddress,\n\t\tuint256 _minCollateral,\n\t\tuint256 _initialCollateral,\n\t\tuint256 _mintingMaximum,\n\t\tuint40 _initPeriodSeconds,\n\t\tuint40 _expirationSeconds,\n\t\tuint40 _challengeSeconds,\n\t\tuint24 _riskPremium,\n\t\tuint256 _liqPrice,\n\t\tuint24 _reservePPM\n\t) public returns (address) {\n\t\t{\n\t\t\trequire(_riskPremium <= 1000000);\n\t\t\trequire(CHALLENGER_REWARD <= _reservePPM && _reservePPM <= 1000000);\n\t\t\trequire(IERC20(_collateralAddress).decimals() <= 24); // leaves 12 digits for price\n\t\t\tuint256 invalidAmount = IERC20(_collateralAddress).totalSupply() + 1;\n\t\t\ttry IERC20(_collateralAddress).transfer(address(0x123), invalidAmount) {\n\t\t\t\trevert IncompatibleCollateral(); // we need a collateral that reverts on failed transfers\n\t\t\t} catch Error(string memory /*reason*/) {} catch Panic(uint /*errorCode*/) {} catch (bytes memory /*lowLevelData*/) {}\n\t\t\tif (_initialCollateral < _minCollateral) revert InsufficientCollateral();\n\t\t\tif (_minCollateral * _liqPrice < 5000 ether * 10 ** 18) revert InsufficientCollateral(); // must start with at least 5000 ZCHF worth of collateral\n\t\t}\n\t\tIPosition pos = IPosition(\n\t\t\tPOSITION_FACTORY.createNewPosition(\n\t\t\t\tmsg.sender,\n\t\t\t\taddress(zchf),\n\t\t\t\t_collateralAddress,\n\t\t\t\t_minCollateral,\n\t\t\t\t_mintingMaximum,\n\t\t\t\t_initPeriodSeconds,\n\t\t\t\t_expirationSeconds,\n\t\t\t\t_challengeSeconds,\n\t\t\t\t_riskPremium,\n\t\t\t\t_liqPrice,\n\t\t\t\t_reservePPM\n\t\t\t)\n\t\t);\n\t\tzchf.registerPosition(address(pos));\n\t\tzchf.collectProfits(msg.sender, OPENING_FEE);\n\t\tIERC20(_collateralAddress).transferFrom(msg.sender, address(pos), _initialCollateral);\n\n\t\temit PositionOpened(msg.sender, address(pos), address(pos), _collateralAddress);\n\t\treturn address(pos);\n\t}\n\n\tfunction clone(address parent, uint256 _initialCollateral, uint256 _initialMint, uint40 expiration) public returns (address) {\n\t\treturn clone(msg.sender, parent, _initialCollateral, _initialMint, expiration);\n\t}\n\n\t/**\n\t * @notice Clones an existing position and immediately tries to mint the specified amount using the given collateral.\n\t * @dev This needs an allowance to be set on the collateral contract such that the minting hub can get the collateral.\n\t */\n\tfunction clone(\n\t\taddress owner,\n\t\taddress parent,\n\t\tuint256 _initialCollateral,\n\t\tuint256 _initialMint,\n\t\tuint40 expiration\n\t) public validPos(parent) returns (address) {\n\t\taddress pos = POSITION_FACTORY.clonePosition(parent);\n\t\tIPosition child = IPosition(pos);\n\t\tchild.initialize(parent, expiration);\n\t\tzchf.registerPosition(pos);\n\t\tIERC20 collateral = child.collateral();\n\t\tif (_initialCollateral < child.minimumCollateral()) revert InsufficientCollateral();\n\t\tcollateral.transferFrom(msg.sender, pos, _initialCollateral); // collateral must still come from sender for security\n\t\temit PositionOpened(owner, address(pos), parent, address(collateral));\n\t\tchild.mint(owner, _initialMint);\n\t\tOwnable(address(child)).transferOwnership(owner);\n\t\treturn address(pos);\n\t}\n\n\t/**\n\t * @notice Launch a challenge (Dutch auction) on a position\n\t * @param _positionAddr      address of the position we want to challenge\n\t * @param _collateralAmount  amount of the collateral we want to challenge\n\t * @param minimumPrice       position.price() to guard against the minter fruntrunning with a price change\n\t * @return index of the challenge in challenge-array\n\t */\n\tfunction challenge(\n\t\taddress _positionAddr,\n\t\tuint256 _collateralAmount,\n\t\tuint256 minimumPrice\n\t) external validPos(_positionAddr) returns (uint256) {\n\t\tIPosition position = IPosition(_positionAddr);\n\t\t// challenger should be ok if frontrun by owner with a higher price\n\t\t// in case owner fruntruns challenger with small price decrease to prevent challenge,\n\t\t// the challenger should set minimumPrice to market price\n\t\tif (position.price() < minimumPrice) revert UnexpectedPrice();\n\t\tIERC20(position.collateral()).transferFrom(msg.sender, address(this), _collateralAmount);\n\t\tuint256 pos = challenges.length;\n\t\tchallenges.push(Challenge(msg.sender, uint40(block.timestamp), position, _collateralAmount));\n\t\tposition.notifyChallengeStarted(_collateralAmount);\n\t\temit ChallengeStarted(msg.sender, address(position), _collateralAmount, pos);\n\t\treturn pos;\n\t}\n\n\t/**\n\t * @notice Post a bid in ZCHF given an open challenge.\n\t *\n\t * @dev In case that the collateral cannot be transfered back to the challenger (i.e. because the collateral token\n\t * has a blacklist and the challenger is on it), it is possible to postpone the return of the collateral.\n\t *\n\t * @param _challengeNumber  index of the challenge as broadcast in the event\n\t * @param size              how much of the collateral the caller wants to bid for at most\n\t *                          (automatically reduced to the available amount)\n\t * @param postponeCollateralReturn To postpone the return of the collateral to the challenger. Usually false.\n\t */\n\tfunction bid(uint32 _challengeNumber, uint256 size, bool postponeCollateralReturn) external {\n\t\tChallenge memory _challenge = challenges[_challengeNumber];\n\t\t(uint256 liqPrice, uint40 phase) = _challenge.position.challengeData();\n\t\tsize = _challenge.size < size ? _challenge.size : size; // cannot bid for more than the size of the challenge\n\n\t\tif (block.timestamp <= _challenge.start + phase) {\n\t\t\t_avertChallenge(_challenge, _challengeNumber, liqPrice, size);\n\t\t\temit ChallengeAverted(address(_challenge.position), _challengeNumber, size);\n\t\t} else {\n\t\t\t_returnChallengerCollateral(_challenge, _challengeNumber, size, postponeCollateralReturn);\n\t\t\t(uint256 transferredCollateral, uint256 offer) = _finishChallenge(_challenge, liqPrice, phase, size);\n\t\t\temit ChallengeSucceeded(address(_challenge.position), _challengeNumber, offer, transferredCollateral, size);\n\t\t}\n\t}\n\n\tfunction _finishChallenge(\n\t\tChallenge memory _challenge,\n\t\tuint256 liqPrice,\n\t\tuint40 phase,\n\t\tuint256 size\n\t) internal returns (uint256, uint256) {\n\t\t// Repayments depend on what was actually minted, whereas bids depend on the available collateral\n\t\t(address owner, uint256 collateral, uint256 repayment, uint32 reservePPM) = _challenge.position.notifyChallengeSucceeded(\n\t\t\tmsg.sender,\n\t\t\tsize\n\t\t);\n\n\t\t// No overflow possible thanks to invariant (col * price <= limit * 10**18)\n\t\t// enforced in Position.setPrice and knowing that collateral <= col.\n\t\tuint256 offer = (_calculatePrice(_challenge.start + phase, phase, liqPrice) * collateral) / 10 ** 18;\n\t\tzchf.transferFrom(msg.sender, address(this), offer); // get money from bidder\n\t\tuint256 reward = (offer * CHALLENGER_REWARD) / 1000_000;\n\t\tzchf.transfer(_challenge.challenger, reward); // pay out the challenger reward\n\t\tuint256 fundsAvailable = offer - reward; // funds available after reward\n\n\t\t// Example: available funds are 90, repayment is 50, reserve 20%. Then 20%*(90-50)=16 are collected as profits\n\t\t// and the remaining 34 are sent to the position owner. If the position owner maxed out debt before the challenge\n\t\t// started and the liquidation price was 100, they would be slightly better off as they would get away with 80\n\t\t// instead of 40+36 = 76 in this example.\n\t\tif (fundsAvailable > repayment) {\n\t\t\t// The excess amount is distributed between the system and the owner using the reserve ratio\n\t\t\t// At this point, we cannot rely on the liquidation price because the challenge might have been started as a\n\t\t\t// response to an unreasonable increase of the liquidation price, such that we have to use this heuristic\n\t\t\t// for excess fund distribution, which make position owners that maxed out their positions slightly better\n\t\t\t// off in comparison to those who did not.\n\t\t\tuint256 profits = (reservePPM * (fundsAvailable - repayment)) / 1000_000;\n\t\t\tzchf.collectProfits(address(this), profits);\n\t\t\tzchf.transfer(owner, fundsAvailable - repayment - profits);\n\t\t} else if (fundsAvailable < repayment) {\n\t\t\tzchf.coverLoss(address(this), repayment - fundsAvailable); // ensure we have enough to pay everything\n\t\t}\n\t\tzchf.burnWithoutReserve(repayment, reservePPM); // Repay the challenged part, example: 50 ZCHF leading to 10 ZCHf in implicit profits\n\t\treturn (collateral, offer);\n\t}\n\n\tfunction _avertChallenge(Challenge memory _challenge, uint32 number, uint256 liqPrice, uint256 size) internal {\n\t\trequire(block.timestamp != _challenge.start); // do not allow to avert the challenge in the same transaction, see CS-ZCHF-037\n\t\tif (msg.sender == _challenge.challenger) {\n\t\t\t// allow challenger to cancel challenge without paying themselves\n\t\t} else {\n\t\t\tzchf.transferFrom(msg.sender, _challenge.challenger, (size * liqPrice) / (10 ** 18));\n\t\t}\n\n\t\t_challenge.position.notifyChallengeAverted(size);\n\t\t_challenge.position.collateral().transfer(msg.sender, size);\n\t\tif (size < _challenge.size) {\n\t\t\tchallenges[number].size = _challenge.size - size;\n\t\t} else {\n\t\t\trequire(size == _challenge.size);\n\t\t\tdelete challenges[number];\n\t\t}\n\t}\n\n\t/**\n\t * @notice Returns 'amount' of the collateral to the challenger and reduces or deletes the relevant challenge.\n\t */\n\tfunction _returnChallengerCollateral(Challenge memory _challenge, uint32 number, uint256 amount, bool postpone) internal {\n\t\t_returnCollateral(_challenge.position.collateral(), _challenge.challenger, amount, postpone);\n\t\tif (_challenge.size == amount) {\n\t\t\t// bid on full amount\n\t\t\tdelete challenges[number];\n\t\t} else {\n\t\t\t// bid on partial amount\n\t\t\tchallenges[number].size -= amount;\n\t\t}\n\t}\n\n\t/**\n\t * @notice Calculates the current Dutch auction price.\n\t * @dev Starts at the full price at time 'start' and linearly goes to 0 as 'phase2' passes.\n\t */\n\tfunction _calculatePrice(uint40 start, uint40 phase2, uint256 liqPrice) internal view returns (uint256) {\n\t\tuint40 timeNow = uint40(block.timestamp);\n\t\tif (timeNow <= start) {\n\t\t\treturn liqPrice;\n\t\t} else if (timeNow >= start + phase2) {\n\t\t\treturn 0;\n\t\t} else {\n\t\t\tuint256 timeLeft = phase2 - (timeNow - start);\n\t\t\treturn (liqPrice / phase2) * timeLeft;\n\t\t}\n\t}\n\n\t/**\n\t * @notice Get the price per unit of the collateral for the given challenge.\n\t * @dev The price comes with (36-collateral.decimals()) digits, such that multiplying it with the\n\t * raw collateral amount always yields a price with 36 digits, or 18 digits after dividing by 10**18 again.\n\t */\n\tfunction price(uint32 challengeNumber) public view returns (uint256) {\n\t\tChallenge memory _challenge = challenges[challengeNumber];\n\t\tif (_challenge.challenger == address(0x0)) {\n\t\t\treturn 0;\n\t\t} else {\n\t\t\t(uint256 liqPrice, uint40 phase) = _challenge.position.challengeData();\n\t\t\treturn _calculatePrice(_challenge.start + phase, phase, liqPrice);\n\t\t}\n\t}\n\n\t/**\n\t * @notice Challengers can call this method to withdraw collateral whose return was postponed.\n\t */\n\tfunction returnPostponedCollateral(address collateral, address target) external {\n\t\tuint256 amount = pendingReturns[collateral][msg.sender];\n\t\tdelete pendingReturns[collateral][msg.sender];\n\t\tIERC20(collateral).transfer(target, amount);\n\t}\n\n\tfunction _returnCollateral(IERC20 collateral, address recipient, uint256 amount, bool postpone) internal {\n\t\tif (postpone) {\n\t\t\t// Postponing helps in case the challenger was blacklisted or otherwise cannot receive at the moment.\n\t\t\tpendingReturns[address(collateral)][recipient] += amount;\n\t\t\temit PostPonedReturn(address(collateral), recipient, amount);\n\t\t} else {\n\t\t\tcollateral.transfer(recipient, amount); // return the challenger's collateral\n\t\t}\n\t}\n\n\t/**\n\t * The applicable purchase price when forcing the sale of collateral of an expired position.\n\t *\n\t * The price starts at 10x the liquidation price at the expiration time, linearly declines to\n\t * 1x liquidation price over the course of one challenge period, and then linearly declines\n\t * less steeply to 0 over the course of another challenge period.\n\t */\n\tfunction expiredPurchasePrice(IPosition pos) public view returns (uint256) {\n\t\tuint256 liqprice = pos.price();\n\t\tuint256 expiration = pos.expiration();\n\t\tif (block.timestamp <= expiration) {\n\t\t\treturn EXPIRED_PRICE_FACTOR * liqprice;\n\t\t} else {\n\t\t\tuint256 challengePeriod = pos.challengePeriod();\n\t\t\tuint256 timePassed = block.timestamp - expiration;\n\t\t\tif (timePassed <= challengePeriod) {\n\t\t\t\t// from 10x liquidation price to 1x in first phase\n\t\t\t\tuint256 timeLeft = challengePeriod - timePassed;\n\t\t\t\treturn liqprice + (((EXPIRED_PRICE_FACTOR - 1) * liqprice) / challengePeriod) * timeLeft;\n\t\t\t} else if (timePassed < 2 * challengePeriod) {\n\t\t\t\t// from 1x liquidation price to 0 in second phase\n\t\t\t\tuint256 timeLeft = 2 * challengePeriod - timePassed;\n\t\t\t\treturn (liqprice / challengePeriod) * timeLeft;\n\t\t\t} else {\n\t\t\t\t// get collateral for free after both phases passed\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Buys up to the desired amount of the collateral asset from the given expired position using\n\t * the applicable 'expiredPurchasePrice' in that instant.\n\t */\n\tfunction buyExpiredCollateral(IPosition pos, uint256 upToAmount) external returns (uint256) {\n\t\tuint256 max = pos.collateral().balanceOf(address(pos));\n\t\tuint256 amount = upToAmount > max ? max : upToAmount;\n\t\tuint256 forceSalePrice = expiredPurchasePrice(pos);\n\t\tuint256 costs = (forceSalePrice * amount) / 10 ** 18;\n\t\tpos.forceSale(msg.sender, amount, costs);\n\t\temit ForcedSale(address(pos), amount, forceSalePrice);\n\t\treturn amount;\n\t}\n}\n","deployed_bytecode":"0x608060405234801561001057600080fd5b506004361061010b5760003560e01c80637eb81bb3116100a2578063c14a9f0511610071578063c14a9f0514610281578063c4d4803a14610294578063e6ac5ea4146102bb578063e6ca1df2146102ce578063e85cde6f146102e157600080fd5b80637eb81bb3146102055780638f1d37761461021a5780639d8ff2a614610265578063af5806b61461027857600080fd5b80632e2c6314116100de5780632e2c63141461018d5780632f09177d146101a057806335a4b349146101c7578063643745fb146101da57600080fd5b80630f2f8e861461011057806325e281241461012b5780632bf78dd81461013e5780632c4e722e1461014e575b600080fd5b610118600a81565b6040519081526020015b60405180910390f35b6101186101393660046122bc565b6102f4565b610118683635c9adc5dea0000081565b6101757f0000000000000000000000003bf301b0e2003e75a3e86ab82bd1eff6a9dfb2ae81565b6040516001600160a01b039091168152602001610122565b61017561019b3660046122fb565b6104c8565b6101757f000000000000000000000000ad0107d3da540fd54b1931735b65110c909ea6b681565b6101756101d536600461235d565b6104e0565b6101186101e8366004612403565b600160209081526000928352604080842090915290825290205481565b61021861021336600461245c565b610a41565b005b61022d61022836600461249e565b610c39565b604080516001600160a01b03958616815264ffffffffff94909416602085015291909316908201526060810191909152608001610122565b6101756102733660046124b7565b610c8b565b610118614e2081565b61011861028f366004612516565b61112f565b6101757f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb81565b6101186102c936600461254b565b6114e2565b6101186102dc366004612568565b6116ef565b6102186102ef366004612403565b6117fc565b600080836001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610335573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103599190612585565b6040516370a0823160e01b81526001600160a01b03868116600483015291909116906370a0823190602401602060405180830381865afa1580156103a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103c591906125a2565b905060008184116103d657836103d8565b815b905060006103e5866114e2565b90506000670de0b6b3a76400006103fc84846125d1565b61040691906125e8565b60405163211d798360e01b815233600482015260248101859052604481018290529091506001600160a01b0388169063211d798390606401600060405180830381600087803b15801561045857600080fd5b505af115801561046c573d6000803e3d6000fd5b5050604080516001600160a01b038b168152602081018790529081018590527f67a660133c1fb4c0bb0480a5e4a9919216684052f13f0713e88fa2fbbc81d0829250606001905060405180910390a15090925050505b92915050565b60006104d73386868686610c8b565b95945050505050565b6000620f42408462ffffff1611156104f757600080fd5b8162ffffff16614e20111580156105165750620f42408262ffffff1611155b61051f57600080fd5b60188b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561055f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610583919061260a565b60ff16111561059157600080fd5b60008b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f591906125a2565b61060090600161262d565b60405163a9059cbb60e01b81529091506001600160a01b038d169063a9059cbb9061063390610123908590600401612640565b6020604051808303816000875af192505050801561066e575060408051601f3d908101601f1916820190925261066b91810190612659565b60015b6106ee5761067a612676565b806308c379a00361069f575061068e6126cd565b8061069957506106b8565b50610708565b634e487b71036106b8576106b1612757565b9061069957505b3d8080156106e2576040519150601f19603f3d011682016040523d82523d6000602084013e6106e7565b606091505b5050610708565b506040516343befb6960e11b815260040160405180910390fd5b8a8a101561072957604051633a23d82560e01b815260040160405180910390fd5b700eb194f8e1ae525fd5dcfab08000000000610745858d6125d1565b101561076457604051633a23d82560e01b815260040160405180910390fd5b50604051632ec127bb60e11b81523360048201526001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb811660248301528c81166044830152606482018c9052608482018a905264ffffffffff89811660a484015288811660c4840152871660e483015262ffffff868116610104840152610124830186905284166101448301526000917f000000000000000000000000728310feaca72dc46cd5bf7d739556d5668472ba90911690635d824f7690610164016020604051808303816000875af1158015610849573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086d9190612585565b60405163b52c696d60e01b81526001600160a01b0380831660048301529192507f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb9091169063b52c696d90602401600060405180830381600087803b1580156108d557600080fd5b505af11580156108e9573d6000803e3d6000fd5b505060405163375edc7760e11b81526001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb169250636ebdb8ee9150610944903390683635c9adc5dea0000090600401612640565b600060405180830381600087803b15801561095e57600080fd5b505af1158015610972573d6000803e3d6000fd5b50506040516323b872dd60e01b81526001600160a01b038f1692506323b872dd91506109a690339085908f90600401612777565b6020604051808303816000875af11580156109c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109e99190612659565b50604080516001600160a01b03808416808352908f1660208301529133917fc9b570ab9d98bdf3e38a40fd71b20edafca42449f23ca51f0bdcbf40e8ffe175910160405180910390a39b9a5050505050505050505050565b6000808463ffffffff1681548110610a5b57610a5b61279b565b600091825260208083206040805160808101825260039490940290910180546001600160a01b038082168652600160a01b90910464ffffffffff169385019390935260018101549092168382018190526002909201546060840152805163194c325560e11b81528151939550849363329864aa926004808401939192918290030181865afa158015610af1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b1591906127b1565b9150915084836060015110610b2a5784610b30565b82606001515b9450808360200151610b4291906127d6565b64ffffffffff164211610baf57610b5b83878488611892565b604080840151815163ffffffff89168152602081018890526001600160a01b03909116917f1eee30d91b773ac47d7485a3acb6bcd8c7c9cd8d95301b1af361baf5f0991d2e910160405180910390a2610c31565b610bbb83878787611b5b565b600080610bca8585858a611c6e565b604080880151815163ffffffff8d16815260208101849052918201849052606082018b90529294509092506001600160a01b03909116907f7d3a26e8d43c5b70f86266bfa26c212e3c097716ff7240ccb6a9034e48754e239060800160405180910390a250505b505050505050565b60008181548110610c4957600080fd5b60009182526020909120600390910201805460018201546002909201546001600160a01b038083169450600160a01b90920464ffffffffff1692919091169084565b60405163aa5dd7f160e01b81526001600160a01b038581166004830152600091869130917f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb9091169063aa5dd7f190602401602060405180830381865afa158015610cfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1e9190612585565b6001600160a01b031614610d45576040516369229ba760e01b815260040160405180910390fd5b604051630167d12360e71b81526001600160a01b0387811660048301526000917f000000000000000000000000728310feaca72dc46cd5bf7d739556d5668472ba9091169063b3e89180906024016020604051808303816000875af1158015610db2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd69190612585565b6040516336a3a4fd60e11b81526001600160a01b03898116600483015264ffffffffff871660248301529192508291821690636d4749fa90604401600060405180830381600087803b158015610e2b57600080fd5b505af1158015610e3f573d6000803e3d6000fd5b505060405163b52c696d60e01b81526001600160a01b0385811660048301527f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb16925063b52c696d9150602401600060405180830381600087803b158015610ea657600080fd5b505af1158015610eba573d6000803e3d6000fd5b505050506000816001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610efe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f229190612585565b9050816001600160a01b031663babe7c746040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f8691906125a2565b881015610fa657604051633a23d82560e01b815260040160405180910390fd5b6040516323b872dd60e01b81526001600160a01b038216906323b872dd90610fd690339087908d90600401612777565b6020604051808303816000875af1158015610ff5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110199190612659565b50604080516001600160a01b038b81168252838116602083015280861692908d16917fc9b570ab9d98bdf3e38a40fd71b20edafca42449f23ca51f0bdcbf40e8ffe175910160405180910390a36040516340c10f1960e01b81526001600160a01b038316906340c10f1990611094908d908b90600401612640565b600060405180830381600087803b1580156110ae57600080fd5b505af11580156110c2573d6000803e3d6000fd5b505060405163f2fde38b60e01b81526001600160a01b038d811660048301528516925063f2fde38b9150602401600060405180830381600087803b15801561110957600080fd5b505af115801561111d573d6000803e3d6000fd5b50949c9b505050505050505050505050565b60405163aa5dd7f160e01b81526001600160a01b038481166004830152600091859130917f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb9091169063aa5dd7f190602401602060405180830381865afa15801561119e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c29190612585565b6001600160a01b0316146111e9576040516369229ba760e01b815260040160405180910390fd5b600085905083816001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561122d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061125191906125a2565b101561127057604051630d57523160e11b815260040160405180910390fd5b806001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112d29190612585565b6001600160a01b03166323b872dd3330886040518463ffffffff1660e01b815260040161130193929190612777565b6020604051808303816000875af1158015611320573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113449190612659565b50600080546040805160808101825233815264ffffffffff428116602083019081526001600160a01b03878116848601818152606086018e8152600189018a559880529451600388027f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563810180549551909616600160a01b026001600160c81b0319909516918416919091179390931790935592517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56482018054919094166001600160a01b0319919091161790925593517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56590910155516342d0204160e01b81529091906342d020419061146090899060040190815260200190565b600060405180830381600087803b15801561147a57600080fd5b505af115801561148e573d6000803e3d6000fd5b505060408051898152602081018590526001600160a01b03861693503392507fc4b384b2c5ca32c8e77081f4083be594a1ea9ba34f208a9f9a458f70608585f5910160405180910390a39695505050505050565b600080826001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611523573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154791906125a2565b90506000836001600160a01b0316634665096d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611589573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ad91906127fb565b64ffffffffff1690508042116115d0576115c882600a6125d1565b949350505050565b6000846001600160a01b031663f3f480d96040518163ffffffff1660e01b8152600401602060405180830381865afa158015611610573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061163491906127fb565b64ffffffffff16905060006116498342612818565b90508181116116a257600061165e8284612818565b905080838661166f6001600a612818565b61167991906125d1565b61168391906125e8565b61168d91906125d1565b611697908661262d565b979650505050505050565b6116ad8260026125d1565b8110156116e3576000816116c28460026125d1565b6116cc9190612818565b9050806116d984876125e8565b61169791906125d1565b50600095945050505050565b60008060008363ffffffff168154811061170b5761170b61279b565b60009182526020918290206040805160808101825260039390930290910180546001600160a01b03808216808652600160a01b90920464ffffffffff169585019590955260018201549094169183019190915260020154606082015291506117765750600092915050565b60008082604001516001600160a01b031663329864aa6040518163ffffffff1660e01b81526004016040805180830381865afa1580156117ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117de91906127b1565b915091506104d78184602001516117f591906127d6565b8284612112565b6001600160a01b038216600081815260016020908152604080832033845290915280822080549290555163a9059cbb60e01b815290919063a9059cbb906118499085908590600401612640565b6020604051808303816000875af1158015611868573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061188c9190612659565b50505050565b836020015164ffffffffff1642036118a957600080fd5b83516001600160a01b0316331461196f5783516001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb16906323b872dd903390670de0b6b3a764000061190287876125d1565b61190c91906125e8565b6040518463ffffffff1660e01b815260040161192a93929190612777565b6020604051808303816000875af1158015611949573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061196d9190612659565b505b83604001516001600160a01b03166349746f10826040518263ffffffff1660e01b81526004016119a191815260200190565b600060405180830381600087803b1580156119bb57600080fd5b505af11580156119cf573d6000803e3d6000fd5b5050505083604001516001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a399190612585565b6001600160a01b031663a9059cbb33836040518363ffffffff1660e01b8152600401611a66929190612640565b6020604051808303816000875af1158015611a85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aa99190612659565b508360600151811015611af957808460600151611ac69190612818565b60008463ffffffff1681548110611adf57611adf61279b565b90600052602060002090600302016002018190555061188c565b83606001518114611b0957600080fd5b60008363ffffffff1681548110611b2257611b2261279b565b60009182526020822060039091020180546001600160c81b03191681556001810180546001600160a01b03191690556002015550505050565b611bcd84604001516001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ba0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bc49190612585565b855184846121a0565b81846060015103611c295760008363ffffffff1681548110611bf157611bf161279b565b60009182526020822060039091020180546001600160c81b03191681556001810180546001600160a01b03191690556002015561188c565b8160008463ffffffff1681548110611c4357611c4361279b565b90600052602060002090600302016002016000828254611c639190612818565b909155505050505050565b60008060008060008089604001516001600160a01b03166320aa9e7233896040518363ffffffff1660e01b8152600401611ca9929190612640565b6080604051808303816000875af1158015611cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cec919061282b565b93509350935093506000670de0b6b3a764000084611d1a8b8e60200151611d1391906127d6565b8c8e612112565b611d2491906125d1565b611d2e91906125e8565b6040516323b872dd60e01b81529091506001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb16906323b872dd90611d8190339030908690600401612777565b6020604051808303816000875af1158015611da0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dc49190612659565b506000620f4240611dd7614e20846125d1565b611de191906125e8565b8c5160405163a9059cbb60e01b81529192506001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb169163a9059cbb91611e32918590600401612640565b6020604051808303816000875af1158015611e51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e759190612659565b506000611e828284612818565b905084811115611fe7576000620f4240611e9c8784612818565b611eac9063ffffffff88166125d1565b611eb691906125e8565b60405163375edc7760e11b81529091506001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb1690636ebdb8ee90611f079030908590600401612640565b600060405180830381600087803b158015611f2157600080fd5b505af1158015611f35573d6000803e3d6000fd5b505050507f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb6001600160a01b031663a9059cbb89838986611f769190612818565b611f809190612818565b6040518363ffffffff1660e01b8152600401611f9d929190612640565b6020604051808303816000875af1158015611fbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fe09190612659565b5050612079565b84811015612079576001600160a01b037f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb1663d1fa5e98306120298489612818565b6040518363ffffffff1660e01b8152600401612046929190612640565b600060405180830381600087803b15801561206057600080fd5b505af1158015612074573d6000803e3d6000fd5b505050505b6040516363b20c3360e11b81526004810186905263ffffffff851660248201527f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb6001600160a01b03169063c764186690604401600060405180830381600087803b1580156120e757600080fd5b505af11580156120fb573d6000803e3d6000fd5b50979f949e50939c50505050505050505050505050565b60004264ffffffffff8086169082161161212f5782915050612199565b61213984866127d6565b64ffffffffff168164ffffffffff1610612157576000915050612199565b6000612163868361286c565b61216d908661286c565b64ffffffffff169050808564ffffffffff168561218a91906125e8565b61219491906125d1565b925050505b9392505050565b801561222c576001600160a01b038085166000908152600160209081526040808320938716835292905290812080548492906121dd90849061262d565b92505081905550826001600160a01b03167f8ab298b78a235f73eee230f82012c0cf4db76003eaabd16a0195f112e7d625c8858460405161221f929190612640565b60405180910390a261188c565b60405163a9059cbb60e01b81526001600160a01b0385169063a9059cbb9061225a9086908690600401612640565b6020604051808303816000875af1158015612279573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061229d9190612659565b5050505050565b6001600160a01b03811681146122b957600080fd5b50565b600080604083850312156122cf57600080fd5b82356122da816122a4565b946020939093013593505050565b64ffffffffff811681146122b957600080fd5b6000806000806080858703121561231157600080fd5b843561231c816122a4565b93506020850135925060408501359150606085013561233a816122e8565b939692955090935050565b803562ffffff8116811461235857600080fd5b919050565b6000806000806000806000806000806101408b8d03121561237d57600080fd5b8a35612388816122a4565b995060208b0135985060408b0135975060608b0135965060808b01356123ad816122e8565b955060a08b01356123bd816122e8565b945060c08b01356123cd816122e8565b93506123db60e08c01612345565b92506101008b013591506123f26101208c01612345565b90509295989b9194979a5092959850565b6000806040838503121561241657600080fd5b8235612421816122a4565b91506020830135612431816122a4565b809150509250929050565b63ffffffff811681146122b957600080fd5b80151581146122b957600080fd5b60008060006060848603121561247157600080fd5b833561247c8161243c565b92506020840135915060408401356124938161244e565b809150509250925092565b6000602082840312156124b057600080fd5b5035919050565b600080600080600060a086880312156124cf57600080fd5b85356124da816122a4565b945060208601356124ea816122a4565b935060408601359250606086013591506080860135612508816122e8565b809150509295509295909350565b60008060006060848603121561252b57600080fd5b8335612536816122a4565b95602085013595506040909401359392505050565b60006020828403121561255d57600080fd5b8135612199816122a4565b60006020828403121561257a57600080fd5b81356121998161243c565b60006020828403121561259757600080fd5b8151612199816122a4565b6000602082840312156125b457600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176104c2576104c26125bb565b60008261260557634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561261c57600080fd5b815160ff8116811461219957600080fd5b808201808211156104c2576104c26125bb565b6001600160a01b03929092168252602082015260400190565b60006020828403121561266b57600080fd5b81516121998161244e565b600060033d111561268f5760046000803e5060005160e01c5b90565b601f8201601f1916810167ffffffffffffffff811182821017156126c657634e487b7160e01b600052604160045260246000fd5b6040525050565b600060443d10156126db5790565b6040516003193d81016004833e81513d67ffffffffffffffff816024840111818411171561270b57505050505090565b82850191508151818111156127235750505050505090565b843d870101602082850101111561273d5750505050505090565b61274c60208286010187612692565b509095945050505050565b60008060233d1115612773576020600460003e50506000516001905b9091565b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052603260045260246000fd5b600080604083850312156127c457600080fd5b825191506020830151612431816122e8565b64ffffffffff8181168382160190808211156127f4576127f46125bb565b5092915050565b60006020828403121561280d57600080fd5b8151612199816122e8565b818103818111156104c2576104c26125bb565b6000806000806080858703121561284157600080fd5b845161284c816122a4565b809450506020850151925060408501519150606085015161233a8161243c565b64ffffffffff8281168282160390808211156127f4576127f46125bb56fea2646970667358221220973c130c3785409a8c924be0c412e1759d2bcde2b319671050f5005dde7e565c64736f6c63430008140033","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":null,"decoded_constructor_args":[["0xB58E61C3098d85632Df34EecfB899A1Ed80921cB",{"internalType":"address","name":"_zchf","type":"address"}],["0x3BF301B0e2003E75A3e86AB82bD1EFF6A9dFB2aE",{"internalType":"address","name":"_leadrate","type":"address"}],["0xAD0107D3Da540Fd54b1931735b65110C909ea6B6",{"internalType":"address","name":"_roller","type":"address"}],["0x728310FeaCa72dc46cD5BF7d739556D5668472BA",{"internalType":"address","name":"_factory","type":"address"}]],"compiler_version":"0.8.20+commit.a1b79de6","is_verified_via_verifier_alliance":true,"verified_at":"2025-06-25T18:40:51.570983Z","implementations":[],"proxy_type":null,"external_libraries":[],"creation_bytecode":"0x6101006040523480156200001257600080fd5b5060405162002a2f38038062002a2f833981016040819052620000359162000076565b6001600160a01b0393841660a05291831660e0529082166080521660c052620000d3565b80516001600160a01b03811681146200007157600080fd5b919050565b600080600080608085870312156200008d57600080fd5b620000988562000059565b9350620000a86020860162000059565b9250620000b86040860162000059565b9150620000c86060860162000059565b905092959194509250565b60805160a05160c05160e0516128c06200016f6000396000610153015260006101a5015260008181610299015281816107820152818161088f0152818161090201528181610cb101528181610e6001528181611155015281816118c601528181611d4801528181611dfd01528181611ed001528181611f3b01528181611ff9015261209b0152600081816107fd0152610d6701526128c06000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c80637eb81bb3116100a2578063c14a9f0511610071578063c14a9f0514610281578063c4d4803a14610294578063e6ac5ea4146102bb578063e6ca1df2146102ce578063e85cde6f146102e157600080fd5b80637eb81bb3146102055780638f1d37761461021a5780639d8ff2a614610265578063af5806b61461027857600080fd5b80632e2c6314116100de5780632e2c63141461018d5780632f09177d146101a057806335a4b349146101c7578063643745fb146101da57600080fd5b80630f2f8e861461011057806325e281241461012b5780632bf78dd81461013e5780632c4e722e1461014e575b600080fd5b610118600a81565b6040519081526020015b60405180910390f35b6101186101393660046122bc565b6102f4565b610118683635c9adc5dea0000081565b6101757f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610122565b61017561019b3660046122fb565b6104c8565b6101757f000000000000000000000000000000000000000000000000000000000000000081565b6101756101d536600461235d565b6104e0565b6101186101e8366004612403565b600160209081526000928352604080842090915290825290205481565b61021861021336600461245c565b610a41565b005b61022d61022836600461249e565b610c39565b604080516001600160a01b03958616815264ffffffffff94909416602085015291909316908201526060810191909152608001610122565b6101756102733660046124b7565b610c8b565b610118614e2081565b61011861028f366004612516565b61112f565b6101757f000000000000000000000000000000000000000000000000000000000000000081565b6101186102c936600461254b565b6114e2565b6101186102dc366004612568565b6116ef565b6102186102ef366004612403565b6117fc565b600080836001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610335573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103599190612585565b6040516370a0823160e01b81526001600160a01b03868116600483015291909116906370a0823190602401602060405180830381865afa1580156103a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103c591906125a2565b905060008184116103d657836103d8565b815b905060006103e5866114e2565b90506000670de0b6b3a76400006103fc84846125d1565b61040691906125e8565b60405163211d798360e01b815233600482015260248101859052604481018290529091506001600160a01b0388169063211d798390606401600060405180830381600087803b15801561045857600080fd5b505af115801561046c573d6000803e3d6000fd5b5050604080516001600160a01b038b168152602081018790529081018590527f67a660133c1fb4c0bb0480a5e4a9919216684052f13f0713e88fa2fbbc81d0829250606001905060405180910390a15090925050505b92915050565b60006104d73386868686610c8b565b95945050505050565b6000620f42408462ffffff1611156104f757600080fd5b8162ffffff16614e20111580156105165750620f42408262ffffff1611155b61051f57600080fd5b60188b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561055f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610583919061260a565b60ff16111561059157600080fd5b60008b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f591906125a2565b61060090600161262d565b60405163a9059cbb60e01b81529091506001600160a01b038d169063a9059cbb9061063390610123908590600401612640565b6020604051808303816000875af192505050801561066e575060408051601f3d908101601f1916820190925261066b91810190612659565b60015b6106ee5761067a612676565b806308c379a00361069f575061068e6126cd565b8061069957506106b8565b50610708565b634e487b71036106b8576106b1612757565b9061069957505b3d8080156106e2576040519150601f19603f3d011682016040523d82523d6000602084013e6106e7565b606091505b5050610708565b506040516343befb6960e11b815260040160405180910390fd5b8a8a101561072957604051633a23d82560e01b815260040160405180910390fd5b700eb194f8e1ae525fd5dcfab08000000000610745858d6125d1565b101561076457604051633a23d82560e01b815260040160405180910390fd5b50604051632ec127bb60e11b81523360048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660248301528c81166044830152606482018c9052608482018a905264ffffffffff89811660a484015288811660c4840152871660e483015262ffffff868116610104840152610124830186905284166101448301526000917f000000000000000000000000000000000000000000000000000000000000000090911690635d824f7690610164016020604051808303816000875af1158015610849573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086d9190612585565b60405163b52c696d60e01b81526001600160a01b0380831660048301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063b52c696d90602401600060405180830381600087803b1580156108d557600080fd5b505af11580156108e9573d6000803e3d6000fd5b505060405163375edc7760e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169250636ebdb8ee9150610944903390683635c9adc5dea0000090600401612640565b600060405180830381600087803b15801561095e57600080fd5b505af1158015610972573d6000803e3d6000fd5b50506040516323b872dd60e01b81526001600160a01b038f1692506323b872dd91506109a690339085908f90600401612777565b6020604051808303816000875af11580156109c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109e99190612659565b50604080516001600160a01b03808416808352908f1660208301529133917fc9b570ab9d98bdf3e38a40fd71b20edafca42449f23ca51f0bdcbf40e8ffe175910160405180910390a39b9a5050505050505050505050565b6000808463ffffffff1681548110610a5b57610a5b61279b565b600091825260208083206040805160808101825260039490940290910180546001600160a01b038082168652600160a01b90910464ffffffffff169385019390935260018101549092168382018190526002909201546060840152805163194c325560e11b81528151939550849363329864aa926004808401939192918290030181865afa158015610af1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b1591906127b1565b9150915084836060015110610b2a5784610b30565b82606001515b9450808360200151610b4291906127d6565b64ffffffffff164211610baf57610b5b83878488611892565b604080840151815163ffffffff89168152602081018890526001600160a01b03909116917f1eee30d91b773ac47d7485a3acb6bcd8c7c9cd8d95301b1af361baf5f0991d2e910160405180910390a2610c31565b610bbb83878787611b5b565b600080610bca8585858a611c6e565b604080880151815163ffffffff8d16815260208101849052918201849052606082018b90529294509092506001600160a01b03909116907f7d3a26e8d43c5b70f86266bfa26c212e3c097716ff7240ccb6a9034e48754e239060800160405180910390a250505b505050505050565b60008181548110610c4957600080fd5b60009182526020909120600390910201805460018201546002909201546001600160a01b038083169450600160a01b90920464ffffffffff1692919091169084565b60405163aa5dd7f160e01b81526001600160a01b038581166004830152600091869130917f00000000000000000000000000000000000000000000000000000000000000009091169063aa5dd7f190602401602060405180830381865afa158015610cfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1e9190612585565b6001600160a01b031614610d45576040516369229ba760e01b815260040160405180910390fd5b604051630167d12360e71b81526001600160a01b0387811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063b3e89180906024016020604051808303816000875af1158015610db2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd69190612585565b6040516336a3a4fd60e11b81526001600160a01b03898116600483015264ffffffffff871660248301529192508291821690636d4749fa90604401600060405180830381600087803b158015610e2b57600080fd5b505af1158015610e3f573d6000803e3d6000fd5b505060405163b52c696d60e01b81526001600160a01b0385811660048301527f000000000000000000000000000000000000000000000000000000000000000016925063b52c696d9150602401600060405180830381600087803b158015610ea657600080fd5b505af1158015610eba573d6000803e3d6000fd5b505050506000816001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610efe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f229190612585565b9050816001600160a01b031663babe7c746040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f8691906125a2565b881015610fa657604051633a23d82560e01b815260040160405180910390fd5b6040516323b872dd60e01b81526001600160a01b038216906323b872dd90610fd690339087908d90600401612777565b6020604051808303816000875af1158015610ff5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110199190612659565b50604080516001600160a01b038b81168252838116602083015280861692908d16917fc9b570ab9d98bdf3e38a40fd71b20edafca42449f23ca51f0bdcbf40e8ffe175910160405180910390a36040516340c10f1960e01b81526001600160a01b038316906340c10f1990611094908d908b90600401612640565b600060405180830381600087803b1580156110ae57600080fd5b505af11580156110c2573d6000803e3d6000fd5b505060405163f2fde38b60e01b81526001600160a01b038d811660048301528516925063f2fde38b9150602401600060405180830381600087803b15801561110957600080fd5b505af115801561111d573d6000803e3d6000fd5b50949c9b505050505050505050505050565b60405163aa5dd7f160e01b81526001600160a01b038481166004830152600091859130917f00000000000000000000000000000000000000000000000000000000000000009091169063aa5dd7f190602401602060405180830381865afa15801561119e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c29190612585565b6001600160a01b0316146111e9576040516369229ba760e01b815260040160405180910390fd5b600085905083816001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561122d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061125191906125a2565b101561127057604051630d57523160e11b815260040160405180910390fd5b806001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112d29190612585565b6001600160a01b03166323b872dd3330886040518463ffffffff1660e01b815260040161130193929190612777565b6020604051808303816000875af1158015611320573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113449190612659565b50600080546040805160808101825233815264ffffffffff428116602083019081526001600160a01b03878116848601818152606086018e8152600189018a559880529451600388027f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563810180549551909616600160a01b026001600160c81b0319909516918416919091179390931790935592517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56482018054919094166001600160a01b0319919091161790925593517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56590910155516342d0204160e01b81529091906342d020419061146090899060040190815260200190565b600060405180830381600087803b15801561147a57600080fd5b505af115801561148e573d6000803e3d6000fd5b505060408051898152602081018590526001600160a01b03861693503392507fc4b384b2c5ca32c8e77081f4083be594a1ea9ba34f208a9f9a458f70608585f5910160405180910390a39695505050505050565b600080826001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611523573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154791906125a2565b90506000836001600160a01b0316634665096d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611589573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ad91906127fb565b64ffffffffff1690508042116115d0576115c882600a6125d1565b949350505050565b6000846001600160a01b031663f3f480d96040518163ffffffff1660e01b8152600401602060405180830381865afa158015611610573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061163491906127fb565b64ffffffffff16905060006116498342612818565b90508181116116a257600061165e8284612818565b905080838661166f6001600a612818565b61167991906125d1565b61168391906125e8565b61168d91906125d1565b611697908661262d565b979650505050505050565b6116ad8260026125d1565b8110156116e3576000816116c28460026125d1565b6116cc9190612818565b9050806116d984876125e8565b61169791906125d1565b50600095945050505050565b60008060008363ffffffff168154811061170b5761170b61279b565b60009182526020918290206040805160808101825260039390930290910180546001600160a01b03808216808652600160a01b90920464ffffffffff169585019590955260018201549094169183019190915260020154606082015291506117765750600092915050565b60008082604001516001600160a01b031663329864aa6040518163ffffffff1660e01b81526004016040805180830381865afa1580156117ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117de91906127b1565b915091506104d78184602001516117f591906127d6565b8284612112565b6001600160a01b038216600081815260016020908152604080832033845290915280822080549290555163a9059cbb60e01b815290919063a9059cbb906118499085908590600401612640565b6020604051808303816000875af1158015611868573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061188c9190612659565b50505050565b836020015164ffffffffff1642036118a957600080fd5b83516001600160a01b0316331461196f5783516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906323b872dd903390670de0b6b3a764000061190287876125d1565b61190c91906125e8565b6040518463ffffffff1660e01b815260040161192a93929190612777565b6020604051808303816000875af1158015611949573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061196d9190612659565b505b83604001516001600160a01b03166349746f10826040518263ffffffff1660e01b81526004016119a191815260200190565b600060405180830381600087803b1580156119bb57600080fd5b505af11580156119cf573d6000803e3d6000fd5b5050505083604001516001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a399190612585565b6001600160a01b031663a9059cbb33836040518363ffffffff1660e01b8152600401611a66929190612640565b6020604051808303816000875af1158015611a85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aa99190612659565b508360600151811015611af957808460600151611ac69190612818565b60008463ffffffff1681548110611adf57611adf61279b565b90600052602060002090600302016002018190555061188c565b83606001518114611b0957600080fd5b60008363ffffffff1681548110611b2257611b2261279b565b60009182526020822060039091020180546001600160c81b03191681556001810180546001600160a01b03191690556002015550505050565b611bcd84604001516001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ba0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bc49190612585565b855184846121a0565b81846060015103611c295760008363ffffffff1681548110611bf157611bf161279b565b60009182526020822060039091020180546001600160c81b03191681556001810180546001600160a01b03191690556002015561188c565b8160008463ffffffff1681548110611c4357611c4361279b565b90600052602060002090600302016002016000828254611c639190612818565b909155505050505050565b60008060008060008089604001516001600160a01b03166320aa9e7233896040518363ffffffff1660e01b8152600401611ca9929190612640565b6080604051808303816000875af1158015611cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cec919061282b565b93509350935093506000670de0b6b3a764000084611d1a8b8e60200151611d1391906127d6565b8c8e612112565b611d2491906125d1565b611d2e91906125e8565b6040516323b872dd60e01b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906323b872dd90611d8190339030908690600401612777565b6020604051808303816000875af1158015611da0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dc49190612659565b506000620f4240611dd7614e20846125d1565b611de191906125e8565b8c5160405163a9059cbb60e01b81529192506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163a9059cbb91611e32918590600401612640565b6020604051808303816000875af1158015611e51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e759190612659565b506000611e828284612818565b905084811115611fe7576000620f4240611e9c8784612818565b611eac9063ffffffff88166125d1565b611eb691906125e8565b60405163375edc7760e11b81529091506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636ebdb8ee90611f079030908590600401612640565b600060405180830381600087803b158015611f2157600080fd5b505af1158015611f35573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb89838986611f769190612818565b611f809190612818565b6040518363ffffffff1660e01b8152600401611f9d929190612640565b6020604051808303816000875af1158015611fbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fe09190612659565b5050612079565b84811015612079576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663d1fa5e98306120298489612818565b6040518363ffffffff1660e01b8152600401612046929190612640565b600060405180830381600087803b15801561206057600080fd5b505af1158015612074573d6000803e3d6000fd5b505050505b6040516363b20c3360e11b81526004810186905263ffffffff851660248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063c764186690604401600060405180830381600087803b1580156120e757600080fd5b505af11580156120fb573d6000803e3d6000fd5b50979f949e50939c50505050505050505050505050565b60004264ffffffffff8086169082161161212f5782915050612199565b61213984866127d6565b64ffffffffff168164ffffffffff1610612157576000915050612199565b6000612163868361286c565b61216d908661286c565b64ffffffffff169050808564ffffffffff168561218a91906125e8565b61219491906125d1565b925050505b9392505050565b801561222c576001600160a01b038085166000908152600160209081526040808320938716835292905290812080548492906121dd90849061262d565b92505081905550826001600160a01b03167f8ab298b78a235f73eee230f82012c0cf4db76003eaabd16a0195f112e7d625c8858460405161221f929190612640565b60405180910390a261188c565b60405163a9059cbb60e01b81526001600160a01b0385169063a9059cbb9061225a9086908690600401612640565b6020604051808303816000875af1158015612279573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061229d9190612659565b5050505050565b6001600160a01b03811681146122b957600080fd5b50565b600080604083850312156122cf57600080fd5b82356122da816122a4565b946020939093013593505050565b64ffffffffff811681146122b957600080fd5b6000806000806080858703121561231157600080fd5b843561231c816122a4565b93506020850135925060408501359150606085013561233a816122e8565b939692955090935050565b803562ffffff8116811461235857600080fd5b919050565b6000806000806000806000806000806101408b8d03121561237d57600080fd5b8a35612388816122a4565b995060208b0135985060408b0135975060608b0135965060808b01356123ad816122e8565b955060a08b01356123bd816122e8565b945060c08b01356123cd816122e8565b93506123db60e08c01612345565b92506101008b013591506123f26101208c01612345565b90509295989b9194979a5092959850565b6000806040838503121561241657600080fd5b8235612421816122a4565b91506020830135612431816122a4565b809150509250929050565b63ffffffff811681146122b957600080fd5b80151581146122b957600080fd5b60008060006060848603121561247157600080fd5b833561247c8161243c565b92506020840135915060408401356124938161244e565b809150509250925092565b6000602082840312156124b057600080fd5b5035919050565b600080600080600060a086880312156124cf57600080fd5b85356124da816122a4565b945060208601356124ea816122a4565b935060408601359250606086013591506080860135612508816122e8565b809150509295509295909350565b60008060006060848603121561252b57600080fd5b8335612536816122a4565b95602085013595506040909401359392505050565b60006020828403121561255d57600080fd5b8135612199816122a4565b60006020828403121561257a57600080fd5b81356121998161243c565b60006020828403121561259757600080fd5b8151612199816122a4565b6000602082840312156125b457600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176104c2576104c26125bb565b60008261260557634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561261c57600080fd5b815160ff8116811461219957600080fd5b808201808211156104c2576104c26125bb565b6001600160a01b03929092168252602082015260400190565b60006020828403121561266b57600080fd5b81516121998161244e565b600060033d111561268f5760046000803e5060005160e01c5b90565b601f8201601f1916810167ffffffffffffffff811182821017156126c657634e487b7160e01b600052604160045260246000fd5b6040525050565b600060443d10156126db5790565b6040516003193d81016004833e81513d67ffffffffffffffff816024840111818411171561270b57505050505090565b82850191508151818111156127235750505050505090565b843d870101602082850101111561273d5750505050505090565b61274c60208286010187612692565b509095945050505050565b60008060233d1115612773576020600460003e50506000516001905b9091565b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052603260045260246000fd5b600080604083850312156127c457600080fd5b825191506020830151612431816122e8565b64ffffffffff8181168382160190808211156127f4576127f46125bb565b5092915050565b60006020828403121561280d57600080fd5b8151612199816122e8565b818103818111156104c2576104c26125bb565b6000806000806080858703121561284157600080fd5b845161284c816122a4565b809450506020850151925060408501519150606085015161233a8161243c565b64ffffffffff8281168282160390808211156127f4576127f46125bb56fea2646970667358221220973c130c3785409a8c924be0c412e1759d2bcde2b319671050f5005dde7e565c64736f6c63430008140033000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb0000000000000000000000003bf301b0e2003e75a3e86ab82bd1eff6a9dfb2ae000000000000000000000000ad0107d3da540fd54b1931735b65110c909ea6b6000000000000000000000000728310feaca72dc46cd5bf7d739556d5668472ba","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":false,"additional_sources":[{"file_path":"contracts/MintingHubV2/PositionRoller.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport '../interface/IERC20.sol';\nimport '../interface/IFrankencoin.sol';\nimport '../utils/Ownable.sol';\nimport '../interface/IReserve.sol';\n\nimport './interfaceV2/IPosition.sol';\n\n/**\n * @title PositionRoller\n *\n * Helper to roll over a debt from an old position to a new one.\n * Both positions should have the same collateral. Otherwise, it does not make much sense.\n */\ncontract PositionRoller {\n\tIFrankencoin private zchf;\n\n\terror NotOwner(address pos);\n\terror NotPosition(address pos);\n\terror Log(uint256, uint256, uint256);\n\n\tevent Roll(address source, uint256 collWithdraw, uint256 repay, address target, uint256 collDeposit, uint256 mint);\n\n\tconstructor(address zchf_) {\n\t\tzchf = IFrankencoin(zchf_);\n\t}\n\n\t/**\n\t * Convenience method to roll and old position into a new one.\n\t *\n\t * Pre-condition is an allowance for the roller to spend the collateral assset on behalf of the caller,\n\t * i.e. one should set collateral.approve(roller, collateral.balanceOf(sourcePosition));\n\t *\n\t * The following is assumed:\n\t * - If the limit of the target position permits, the user wants to roll everything\n\t * - The user does not want to add additional collateral, but excess collateral is returned\n\t * - If not enough can be minted in the new position, it is ok for the roller to use ZCHF from the msg.sender\n\t */\n\tfunction rollFully(IPosition source, IPosition target) external {\n\t\trollFullyWithExpiration(source, target, target.expiration());\n\t}\n\n\t/**\n\t * Like rollFully, but with a custom expiration date for the new position.\n\t */\n\tfunction rollFullyWithExpiration(IPosition source, IPosition target, uint40 expiration) public {\n\t\trequire(source.collateral() == target.collateral());\n\t\tuint256 repay = findRepaymentAmount(source);\n\t\tuint256 mintAmount = target.getMintAmount(repay);\n\t\tuint256 collateralToWithdraw = IERC20(source.collateral()).balanceOf(address(source));\n\t\tuint256 targetPrice = target.price();\n\t\tuint256 depositAmount = (mintAmount * 10 ** 18 + targetPrice - 1) / targetPrice; // round up\n\t\tif (depositAmount > collateralToWithdraw) {\n\t\t\t// If we need more collateral than available from the old position, we opt for taking\n\t\t\t// the missing funds from the caller instead of taking additional collateral from the caller\n\t\t\tdepositAmount = collateralToWithdraw;\n\t\t\tmintAmount = (depositAmount * target.price()) / 10 ** 18; // round down, rest will be taken from caller\n\t\t}\n\t\troll(source, repay, collateralToWithdraw, target, mintAmount, depositAmount, expiration);\n\t}\n\n\t/**\n\t * Doing a binary search is not very efficient, but guaranteed to return a valid result without rounding errors.\n\t * To save gas costs, the frontend can also call this and other methods to calculate the right parameters and\n\t * then call 'roll' directly.\n\t */\n\tfunction findRepaymentAmount(IPosition pos) public view returns (uint256) {\n\t\tuint256 minted = pos.minted();\n\t\tuint24 reservePPM = pos.reserveContribution();\n\t\tif (minted == 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tuint256 higherResult = zchf.calculateFreedAmount(minted, reservePPM);\n\t\tif (higherResult == minted) {\n\t\t\treturn minted;\n\t\t}\n\t\treturn binarySearch(minted, reservePPM, 0, 0, minted, higherResult);\n\t}\n\n\t// max call stack depth is 1024 in solidity. Binary search on 256 bit number takes at most 256 steps, so it should be fine.\n\tfunction binarySearch(\n\t\tuint256 target,\n\t\tuint24 reservePPM,\n\t\tuint256 lowerBound,\n\t\tuint256 lowerResult,\n\t\tuint256 higherBound,\n\t\tuint256 higherResult\n\t) internal view returns (uint256) {\n\t\tuint256 middle = (lowerBound + higherBound) / 2;\n\t\tif (middle == lowerBound) {\n\t\t\treturn higherBound; // we have reached max precision without exact match, return next higher result to be on the safe side\n\t\t} else {\n\t\t\tuint256 middleResult = zchf.calculateFreedAmount(middle, reservePPM);\n\t\t\tif (middleResult == target) {\n\t\t\t\treturn middle;\n\t\t\t} else if (middleResult < target) {\n\t\t\t\treturn binarySearch(target, reservePPM, middle, middleResult, higherBound, higherResult);\n\t\t\t} else {\n\t\t\t\treturn binarySearch(target, reservePPM, lowerBound, lowerResult, middle, middleResult);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Rolls the source position into the target position using a flash loan.\n\t * Both the source and the target position must recognize this roller.\n\t * It is the responsibility of the caller to ensure that both positions are valid contracts.\n\t *\n\t * @param source The source position, must be owned by the msg.sender .\n\t * @param repay The amount to flash loan in order to repay the source position and free up some or all collateral.\n\t * @param collWithdraw Collateral to move from the source position to the msg.sender .\n\t * @param target The target position. If not owned by msg.sender or if it does not have the desired expiration,\n\t *               it is cloned to create a position owned by the msg.sender.\n\t * @param mint The amount to be minted from the target position using collateral from msg.sender.\n\t * @param collDeposit The amount of collateral to be send from msg.sender to the target position.\n\t * @param expiration The desired expiration date for the target position.\n\t */\n\tfunction roll(\n\t\tIPosition source,\n\t\tuint256 repay,\n\t\tuint256 collWithdraw,\n\t\tIPosition target,\n\t\tuint256 mint,\n\t\tuint256 collDeposit,\n\t\tuint40 expiration\n\t) public valid(source) valid(target) own(source) {\n\t\tzchf.mint(address(this), repay); // take a flash loan\n\t\tsource.repay(repay);\n\t\tsource.withdrawCollateral(msg.sender, collWithdraw);\n\t\tif (mint > 0) {\n\t\t\tIERC20 targetCollateral = IERC20(target.collateral());\n\t\t\tif (Ownable(address(target)).owner() != msg.sender || expiration != target.expiration()) {\n\t\t\t\ttargetCollateral.transferFrom(msg.sender, address(this), collDeposit); // get the new collateral\n\t\t\t\ttargetCollateral.approve(target.hub(), collDeposit); // approve the new collateral and clone:\n\t\t\t\ttarget = IPosition(IMintingHub(target.hub()).clone(msg.sender, address(target), collDeposit, mint, expiration));\n\t\t\t} else {\n\t\t\t\t// We can roll into the provided existing position\n\t\t\t\t// We do not verify whether the target position has been created by the known minting hub in order\n\t\t\t\t// to allow positions to be rolled into future versions of the minting hub\n\t\t\t\ttargetCollateral.transferFrom(msg.sender, address(target), collDeposit);\n\t\t\t\ttarget.mint(msg.sender, mint);\n\t\t\t}\n\t\t}\n\t\tzchf.burnFrom(msg.sender, repay); // repay the flash loan\n\t\temit Roll(address(source), collWithdraw, repay, address(target), collDeposit, mint);\n\t}\n\n\tmodifier own(IPosition pos) {\n\t\tif (Ownable(address(pos)).owner() != msg.sender) revert NotOwner(address(pos));\n\t\t_;\n\t}\n\n\tmodifier valid(IPosition pos) {\n\t\tif (zchf.getPositionParent(address(pos)) == address(0x0)) revert NotPosition(address(pos));\n\t\t_;\n\t}\n}\n\ninterface IMintingHub {\n\tfunction clone(\n\t\taddress owner,\n\t\taddress parent,\n\t\tuint256 _initialCollateral,\n\t\tuint256 _initialMint,\n\t\tuint40 expiration\n\t) external returns (address);\n}\n"},{"file_path":"contracts/MintingHubV2/interfaceV2/IPosition.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport '../../interface/IERC20.sol';\n\ninterface IPosition {\n\tfunction hub() external view returns (address);\n\n\tfunction collateral() external view returns (IERC20);\n\n\tfunction minimumCollateral() external view returns (uint256);\n\n\tfunction price() external view returns (uint256);\n\n\tfunction minted() external view returns (uint256);\n\n\tfunction challengedAmount() external view returns (uint256);\n\n\tfunction original() external view returns (address);\n\n\tfunction expiration() external view returns (uint40);\n\n\tfunction cooldown() external view returns (uint40);\n\n\tfunction limit() external view returns (uint256);\n\n\tfunction challengePeriod() external view returns (uint40);\n\n\tfunction start() external view returns (uint40);\n\n\tfunction riskPremiumPPM() external view returns (uint24);\n\n\tfunction reserveContribution() external view returns (uint24);\n\n\tfunction initialize(address parent, uint40 _expiration) external;\n\n\tfunction assertCloneable() external;\n\n\tfunction notifyMint(uint256 mint_) external;\n\n\tfunction notifyRepaid(uint256 repaid_) external;\n\n\tfunction availableForClones() external view returns (uint256);\n\n\tfunction availableForMinting() external view returns (uint256);\n\n\tfunction deny(address[] calldata helpers, string calldata message) external;\n\n\tfunction getUsableMint(uint256 totalMint, bool afterFees) external view returns (uint256);\n\n\tfunction getMintAmount(uint256 usableMint) external view returns (uint256);\n\n\tfunction adjust(uint256 newMinted, uint256 newCollateral, uint256 newPrice) external;\n\n\tfunction adjustPrice(uint256 newPrice) external;\n\n\tfunction mint(address target, uint256 amount) external;\n\n\tfunction calculateCurrentFee() external view returns (uint24);\n\n\tfunction annualInterestPPM() external view returns (uint24);\n\n\tfunction calculateFee(uint256 exp) external view returns (uint24);\n\n\tfunction repay(uint256 amount) external returns (uint256);\n\n\tfunction forceSale(address buyer, uint256 collAmount, uint256 proceeds) external;\n\n\tfunction withdraw(address token, address target, uint256 amount) external;\n\n\tfunction withdrawCollateral(address target, uint256 amount) external;\n\n\tfunction challengeData() external view returns (uint256 liqPrice, uint40 phase);\n\n\tfunction notifyChallengeStarted(uint256 size) external;\n\n\tfunction notifyChallengeAverted(uint256 size) external;\n\n\tfunction notifyChallengeSucceeded(address _bidder, uint256 _size) external returns (address, uint256, uint256, uint32);\n}\n"},{"file_path":"contracts/MintingHubV2/interfaceV2/IPositionFactory.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IPositionFactory {\n\tfunction createNewPosition(\n\t\taddress _owner,\n\t\taddress _zchf,\n\t\taddress _collateral,\n\t\tuint256 _minCollateral,\n\t\tuint256 _initialLimit,\n\t\tuint40 _initPeriod,\n\t\tuint40 _duration,\n\t\tuint40 _challengePeriod,\n\t\tuint24 _riskPremiumPPM,\n\t\tuint256 _liqPrice,\n\t\tuint24 _reserve\n\t) external returns (address);\n\n\tfunction clonePosition(address _parent) external returns (address);\n}\n"},{"file_path":"contracts/interface/IERC20.sol","source_code":"/**\n * SPDX-License-Identifier: MIT\n *\n * Copyright (c) 2016-2019 zOS Global Limited\n *\n */\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP. Does not include\n * the optional functions; to access them see `ERC20Detailed`.\n */\n\ninterface IERC20 {\n    function name() external view returns (string memory);\n\n    function symbol() external view returns (string memory);\n\n    function decimals() external view returns (uint8);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `recipient`.\n     *\n     * Returns always true. Throws error on failure.\n     *\n     * Emits a `Transfer` event.\n     */\n    function transfer(address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through `transferFrom`. This is\n     * zero by default.\n     *\n     * This value can change when `approve` or `transferFrom` are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * > Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an `Approval` event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `sender` to `recipient` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns always true. Throws error on failure.\n     *\n     * Emits a `Transfer` event.\n     */\n    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to `approve`. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"},{"file_path":"contracts/interface/IFrankencoin.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./IReserve.sol\";\n\ninterface IFrankencoin is IERC20 {\n    function suggestMinter(address _minter, uint256 _applicationPeriod, uint256 _applicationFee, string calldata _message) external;\n\n    function registerPosition(address position) external;\n\n    function denyMinter(address minter, address[] calldata helpers, string calldata message) external;\n\n    function reserve() external view returns (IReserve);\n\n    function minterReserve() external view returns (uint256);\n\n    function calculateAssignedReserve(uint256 mintedAmount, uint32 _reservePPM) external view returns (uint256);\n\n    function calculateFreedAmount(uint256 amountExcludingReserve, uint32 reservePPM) external view returns (uint256);\n\n    function equity() external view returns (uint256);\n\n    function isMinter(address minter) external view returns (bool);\n\n    function getPositionParent(address position) external view returns (address);\n\n    function mint(address target, uint256 amount) external;\n\n    function mintWithReserve(address target, uint256 amount, uint32 reservePPM, uint32 feePPM) external;\n\n    function burnFrom(address target, uint256 amount) external;\n\n    function burnWithoutReserve(uint256 amountIncludingReserve, uint32 reservePPM) external;\n\n    function burnFromWithReserve(address payer, uint256 targetTotalBurnAmount, uint32 _reservePPM) external returns (uint256);\n\n    function burnWithReserve(uint256 amountExcludingReserve, uint32 reservePPM) external returns (uint256);\n\n    function coverLoss(address source, uint256 amount) external;\n\n    function collectProfits(address source, uint256 _amount) external;\n}\n"},{"file_path":"contracts/interface/ILeadrate.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ILeadrate {\n   function currentRatePPM() external view returns (uint24);\n   function currentTicks() external view returns (uint64);\n}"},{"file_path":"contracts/interface/IReserve.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\n\ninterface IReserve is IERC20 {\n   function invest(uint256 amount, uint256 expected) external returns (uint256);\n   function checkQualified(address sender, address[] calldata helpers) external view;\n}"},{"file_path":"contracts/utils/Ownable.sol","source_code":"// SPDX-License-Identifier: MIT\n//\n// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol\n//\n// Modifications:\n// - Replaced Context._msgSender() with msg.sender\n// - Made leaner\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n */\ncontract Ownable {\n\n    address public owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    error NotOwner();\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public onlyOwner {\n        _setOwner(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _setOwner(address newOwner) internal {\n        require(newOwner != address(0x0)); // ensure initialization can only done once\n        address oldOwner = owner;\n        owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n\n    function _requireOwner(address sender) internal view {\n        if (owner != sender && owner != address(0x0)) revert NotOwner();\n    }\n\n    modifier onlyOwner() {\n        _requireOwner(msg.sender);\n        _;\n    }\n}\n"}],"certified":false,"conflicting_implementations":null,"abi":[{"inputs":[{"internalType":"address","name":"_zchf","type":"address"},{"internalType":"address","name":"_leadrate","type":"address"},{"internalType":"address","name":"_roller","type":"address"},{"internalType":"address","name":"_factory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"IncompatibleCollateral","type":"error"},{"inputs":[],"name":"InsufficientCollateral","type":"error"},{"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":false,"internalType":"address","name":"pos","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"priceE36MinusDecimals","type":"uint256"}],"name":"ForcedSale","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":"original","type":"address"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"}],"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":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXPIRED_PRICE_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"contract IPosition","name":"pos","type":"address"},{"internalType":"uint256","name":"upToAmount","type":"uint256"}],"name":"buyExpiredCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_positionAddr","type":"address"},{"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"internalType":"uint256","name":"minimumPrice","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":"uint40","name":"start","type":"uint40"},{"internalType":"contract IPosition","name":"position","type":"address"},{"internalType":"uint256","name":"size","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"parent","type":"address"},{"internalType":"uint256","name":"_initialCollateral","type":"uint256"},{"internalType":"uint256","name":"_initialMint","type":"uint256"},{"internalType":"uint40","name":"expiration","type":"uint40"}],"name":"clone","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"parent","type":"address"},{"internalType":"uint256","name":"_initialCollateral","type":"uint256"},{"internalType":"uint256","name":"_initialMint","type":"uint256"},{"internalType":"uint40","name":"expiration","type":"uint40"}],"name":"clone","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPosition","name":"pos","type":"address"}],"name":"expiredPurchasePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":"uint40","name":"_initPeriodSeconds","type":"uint40"},{"internalType":"uint40","name":"_expirationSeconds","type":"uint40"},{"internalType":"uint40","name":"_challengeSeconds","type":"uint40"},{"internalType":"uint24","name":"_riskPremium","type":"uint24"},{"internalType":"uint256","name":"_liqPrice","type":"uint256"},{"internalType":"uint24","name":"_reservePPM","type":"uint24"}],"name":"openPosition","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":[],"name":"rate","outputs":[{"internalType":"contract ILeadrate","name":"","type":"address"}],"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":"roller","outputs":[{"internalType":"contract PositionRoller","name":"","type":"address"}],"stateMutability":"view","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":"0x000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb0000000000000000000000003bf301b0e2003e75a3e86ab82bd1eff6a9dfb2ae000000000000000000000000ad0107d3da540fd54b1931735b65110c909ea6b6000000000000000000000000728310feaca72dc46cd5bf7d739556d5668472ba"}