ETH Price: $2,088.67 (+1.76%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Adjust243927242026-02-05 19:29:479 days ago1770319787IN
0xB55C4808...8A33e87AE
0 ETH0.000154021.49322552
Adjust238194962025-11-17 14:30:4789 days ago1763389847IN
0xB55C4808...8A33e87AE
0 ETH0.000062940.61024572
Adjust233051492025-09-06 16:08:47161 days ago1757174927IN
0xB55C4808...8A33e87AE
0 ETH0.000009120.15647916
Adjust233051402025-09-06 16:06:59161 days ago1757174819IN
0xB55C4808...8A33e87AE
0 ETH0.000008370.15040616
Adjust232798422025-09-03 3:17:23165 days ago1756869443IN
0xB55C4808...8A33e87AE
0 ETH0.00001570.26032484
Adjust222154852025-04-07 7:17:47313 days ago1744010267IN
0xB55C4808...8A33e87AE
0 ETH0.0035346249.11997401
Adjust221695032025-03-31 21:13:11320 days ago1743455591IN
0xB55C4808...8A33e87AE
0 ETH0.000047610.7084622
Adjust219440532025-02-28 9:49:23351 days ago1740736163IN
0xB55C4808...8A33e87AE
0 ETH0.000163352.43149702
Adjust217648022025-02-03 8:30:47376 days ago1738571447IN
0xB55C4808...8A33e87AE
0 ETH0.001097715.5275347

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x3d602d80216967942025-01-24 20:36:59386 days ago1737751019  Contract Creation0 ETH
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Minimal Proxy Contract for 0x49c431454c40ecbf848096f2753b2abc3a699a10

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x1eEd91Ee...cA4254804
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
Position

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion, MIT license

Contract Source Code (Solidity Multiple files format)

File 1 of 8: Position.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import './Ownable.sol';
import './MathUtil.sol';

import './IERC20.sol';
import './ILeadrate.sol';
import './IReserve.sol';
import './IFrankencoin.sol';

import './IPosition.sol';

/**
 * @title Position
 * @notice A collateralized minting position.
 */
contract Position is Ownable, IPosition, MathUtil {
	/**
	 * @notice Note that this contract is intended to be cloned. All clones will share the same values for
	 * the constant and immutable fields, but have their own values for the other fields.
	 */

	/**
	 * @notice The zchf price per unit of the collateral below which challenges succeed, (36 - collateral.decimals) decimals
	 */
	uint256 public price;

	/**
	 * @notice Net minted amount, including reserve.
	 */
	uint256 public minted;

	/**
	 * @notice How much has been minted in total. This variable is only used in the parent position.
	 */
	uint256 private totalMinted;

	uint256 public immutable limit;

	/**
	 * @notice Amount of the collateral that is currently under a challenge.
	 * Used to figure out whether there are pending challenges.
	 */
	uint256 public challengedAmount;

	/**
	 * @notice Challenge period in seconds.
	 */
	uint40 public immutable challengePeriod;

	/**
	 * @notice Timestamp when minting can start and the position no longer denied.
	 */
	uint40 public immutable start;

	/**
	 * @notice End of the latest cooldown. If this is in the future, minting is suspended.
	 */
	uint40 public cooldown;

	/**
	 * @notice Timestamp of the expiration of the position. After expiration, challenges cannot be averted
	 * any more. This is also the basis for fee calculations.
	 */
	uint40 public expiration;

	bool private closed;

	/**
	 * @notice The original position to help identifying clones.
	 */
	address public immutable original;

	/**
	 * @notice Pointer to the minting hub.
	 */
	address public immutable hub;

	/**
	 * @notice The Frankencoin contract.
	 */
	IFrankencoin public immutable zchf;

	/**
	 * @notice The collateral token.
	 */
	IERC20 public immutable override collateral;

	/**
	 * @notice Minimum acceptable collateral amount to prevent dust.
	 */
	uint256 public immutable override minimumCollateral;

	/**
	 * @notice The interest in parts per million per year that is deducted when minting Frankencoins.
	 * To be paid upfront.
	 */
	uint24 public immutable riskPremiumPPM;

	/**
	 * @notice The reserve contribution in parts per million of the minted amount.
	 */
	uint24 public immutable reserveContribution;

	event MintingUpdate(uint256 collateral, uint256 price, uint256 minted);
	event PositionDenied(address indexed sender, string message); // emitted if closed by governance

	error InsufficientCollateral(uint256 needed, uint256 available);
	error TooLate();
	error RepaidTooMuch(uint256 excess);
	error LimitExceeded(uint256 tried, uint256 available);
	error ChallengeTooSmall();
	error Expired(uint40 time, uint40 expiration);
	error Alive();
	error Closed();
	error Hot();
	error Challenged();
	error NotHub();
	error NotOriginal();
	error InvalidExpiration();
	error AlreadyInitialized();

	modifier alive() {
		if (block.timestamp >= expiration) revert Expired(uint40(block.timestamp), expiration);
		_;
	}

	// requires that the position has always been backed by a minimal amount of collateral
	modifier backed() {
		if (isClosed()) revert Closed();
		_;
	}

	modifier expired() {
		if (block.timestamp < expiration) revert Alive();
		_;
	}

	modifier noCooldown() {
		if (block.timestamp <= cooldown) revert Hot();
		_;
	}

	modifier noChallenge() {
		if (challengedAmount > 0) revert Challenged();
		_;
	}

	modifier onlyHub() {
		if (msg.sender != address(hub)) revert NotHub();
		_;
	}

	modifier ownerOrRoller() {
		if (msg.sender != address(IHub(hub).roller())) _requireOwner(msg.sender);
		_;
	}

	/**
	 * @dev See MintingHub.openPosition
	 */
	constructor(
		address _owner,
		address _hub,
		address _zchf,
		address _collateral,
		uint256 _minCollateral,
		uint256 _initialLimit,
		uint40 _initPeriod,
		uint40 _duration,
		uint40 _challengePeriod,
		uint24 _riskPremiumPPM,
		uint256 _liqPrice,
		uint24 _reservePPM
	) {
		require(_initPeriod >= 3 days); // must be at least three days, recommended to use higher values
		_setOwner(_owner);
		original = address(this);
		hub = _hub;
		zchf = IFrankencoin(_zchf);
		collateral = IERC20(_collateral);
		riskPremiumPPM = _riskPremiumPPM;
		reserveContribution = _reservePPM;
		minimumCollateral = _minCollateral;
		challengePeriod = _challengePeriod;
		start = uint40(block.timestamp) + _initPeriod; // at least three days time to deny the position
		cooldown = start;
		expiration = start + _duration;
		limit = _initialLimit;
		_setPrice(_liqPrice, _initialLimit);
	}

	/**
	 * Initialization method for clones.
	 * Can only be called once. Should be called immediately after creating the clone.
	 */
	function initialize(address parent, uint40 _expiration) external onlyHub {
		if (expiration != 0) revert AlreadyInitialized();
		if (_expiration < block.timestamp || _expiration > Position(original).expiration()) revert InvalidExpiration(); // expiration must not be later than original
		expiration = _expiration;
		price = Position(parent).price();
		_setOwner(hub);
	}

	/**
	 * Cloning a position is only allowed if the position is not challenged, not expired and not in cooldown.
	 */
	function assertCloneable() external noChallenge noCooldown alive backed {}

	/**
	 * Notify the original that some amount has been minted.
	 */
	function notifyMint(uint256 mint_) external {
		if (zchf.getPositionParent(msg.sender) != hub) revert NotHub();
		totalMinted += mint_;
	}

	function notifyRepaid(uint256 repaid_) external {
		if (zchf.getPositionParent(msg.sender) != hub) revert NotHub();
		totalMinted -= repaid_;
	}

	/**
	 * Should only be called on the original position.
	 * Better use 'availableForMinting'.
	 */
	function availableForClones() external view returns (uint256) {
		// reserve capacity for the original to the extent the owner provided collateral
		uint256 potential = (_collateralBalance() * price) / ONE_DEC18;
		uint256 unusedPotential = minted > potential ? 0 : potential - minted;
		if (totalMinted + unusedPotential >= limit) {
			return 0;
		} else {
			return limit - totalMinted - unusedPotential;
		}
	}

	/**
	 * The amount available for minting in this position family.
	 *
	 * Does not check if positions are challenged, closed, or under cooldown.
	 */
	function availableForMinting() public view returns (uint256) {
		if (address(this) == original) {
			return limit - totalMinted;
		} else {
			return Position(original).availableForClones();
		}
	}

	/**
	 * @notice Qualified pool share holders can call this method to immediately expire a freshly proposed position.
	 */
	function deny(address[] calldata helpers, string calldata message) external {
		if (block.timestamp >= start) revert TooLate();
		IReserve(zchf.reserve()).checkQualified(msg.sender, helpers);
		_close();
		emit PositionDenied(msg.sender, message);
	}

	/**
	 * Closes the position by putting it into eternal cooldown.
	 * This allows the users to still withdraw the collateral that is left, but never to mint again.
	 */
	function _close() internal {
		closed = true;
	}

	function isClosed() public view returns (bool) {
		return closed;
	}

	/**
	 * @notice This is how much the minter can actually use when minting ZCHF, with the rest being used
	 * assigned to the minter reserve or (if applicable) fees.
	 */
	function getUsableMint(uint256 totalMint, bool afterFees) public view returns (uint256) {
		if (afterFees) {
			return (totalMint * (1000_000 - reserveContribution - calculateCurrentFee())) / 1000_000;
		} else {
			return (totalMint * (1000_000 - reserveContribution)) / 1000_000;
		}
	}

	/**
	 * Returns the corresponding mint amount (disregarding the limit).
	 */
	function getMintAmount(uint256 usableMint) external view returns (uint256) {
		return usableMint == 0 ? 0 : (usableMint * 1000_000 - 1) / (1000_000 - reserveContribution - calculateCurrentFee()) + 1;
	}

	/**
	 * @notice "All in one" function to adjust the outstanding amount of ZCHF, the collateral amount,
	 * and the price in one transaction.
	 */
	function adjust(uint256 newMinted, uint256 newCollateral, uint256 newPrice) external onlyOwner {
		uint256 colbal = _collateralBalance();
		if (newCollateral > colbal) {
			collateral.transferFrom(msg.sender, address(this), newCollateral - colbal);
		}
		// Must be called after collateral deposit, but before withdrawal
		if (newMinted < minted) {
			zchf.burnFromWithReserve(msg.sender, minted - newMinted, reserveContribution);
			_notifyRepaid(minted - newMinted);
		}
		if (newCollateral < colbal) {
			_withdrawCollateral(msg.sender, colbal - newCollateral);
		}
		// Must be called after collateral withdrawal
		if (newMinted > minted) {
			_mint(msg.sender, newMinted - minted, newCollateral);
		}
		if (newPrice != price) {
			_adjustPrice(newPrice);
		}
		emit MintingUpdate(newCollateral, newPrice, newMinted);
	}

	/**
	 * @notice Allows the position owner to adjust the liquidation price as long as there is no pending challenge.
	 * Lowering the liquidation price can be done with immediate effect, given that there is enough collateral.
	 * Increasing the liquidation price triggers a cooldown period of 3 days, during which minting is suspended.
	 */
	function adjustPrice(uint256 newPrice) public onlyOwner {
		_adjustPrice(newPrice);
		emit MintingUpdate(_collateralBalance(), price, minted);
	}

	function _adjustPrice(uint256 newPrice) internal noChallenge alive backed {
		if (newPrice > price) {
			_restrictMinting(3 days);
		} else {
			_checkCollateral(_collateralBalance(), newPrice);
		}
		_setPrice(newPrice, minted + availableForMinting());
	}

	function _setPrice(uint256 newPrice, uint256 bounds) internal {
		require(newPrice * minimumCollateral <= bounds * ONE_DEC18); // sanity check
		price = newPrice;
	}

	function _collateralBalance() internal view returns (uint256) {
		return IERC20(collateral).balanceOf(address(this));
	}

	/**
	 * @notice Mint ZCHF as long as there is no open challenge, the position is not subject to a cooldown,
	 * and there is sufficient collateral.
	 */
	function mint(address target, uint256 amount) public ownerOrRoller {
		uint256 collateralBalance = _collateralBalance();
		_mint(target, amount, collateralBalance);
		emit MintingUpdate(collateralBalance, price, minted);
	}

	/**
	 * The applicable upfront fee in ppm when minting more Frankencoins based on the annual interest rate and
	 * the expiration of the position.
	 */
	function calculateCurrentFee() public view returns (uint24) {
		return calculateFee(expiration);
	}

	/**
	 * The applicable interest rate in ppm when minting more Frankencoins.
	 * It consists on the globally valid interest plus an individual risk premium.
	 */
	function annualInterestPPM() public view returns (uint24) {
		return IHub(hub).rate().currentRatePPM() + riskPremiumPPM;
	}

	/**
	 * The fee in ppm when cloning and minting with the given expiration date.
	 */
	function calculateFee(uint256 exp) public view returns (uint24) {
		uint256 time = block.timestamp < start ? start : block.timestamp;
		uint256 timePassed = exp - time;
		// Time resolution is in the range of minutes for typical interest rates.
		uint256 feePPM = (timePassed * annualInterestPPM()) / 365 days;
		return uint24(feePPM > 1000000 ? 1000000 : feePPM); // fee cannot exceed 100%
	}

	function _mint(address target, uint256 amount, uint256 collateral_) internal noChallenge noCooldown alive backed {
		if (amount > availableForMinting()) revert LimitExceeded(amount, availableForMinting());
		Position(original).notifyMint(amount);
		zchf.mintWithReserve(target, amount, reserveContribution, calculateCurrentFee());
		minted += amount;
		_checkCollateral(collateral_, price);
	}

	function _restrictMinting(uint40 period) internal {
		uint40 horizon = uint40(block.timestamp) + period;
		if (horizon > cooldown) {
			cooldown = horizon;
		}
	}

	/**
	 * @notice Repay some ZCHF. If too much is repaid, the call fails.
	 * It is possible to repay while there are challenges, but the collateral is locked until all is clear again.
	 *
	 * The repaid amount should fulfill the following equation in order to close the position,
	 * i.e. bring the minted amount to 0:
	 * minted = amount + zchf.calculateAssignedReserve(amount, reservePPM)
	 *
	 * Under normal circumstances, this implies:
	 * amount = minted * (1000000 - reservePPM)
	 *
	 * E.g. if minted is 50 and reservePPM is 200000, it is necessary to repay 40 to be able to close the position.
	 */
	function repay(uint256 amount) public returns (uint256) {
		IERC20(zchf).transferFrom(msg.sender, address(this), amount);
		uint256 actuallyRepaid = IFrankencoin(zchf).burnWithReserve(amount, reserveContribution);
		_notifyRepaid(actuallyRepaid);
		emit MintingUpdate(_collateralBalance(), price, minted);
		return actuallyRepaid;
	}

	function _notifyRepaid(uint256 amount) internal {
		if (amount > minted) revert RepaidTooMuch(amount - minted);
		Position(original).notifyRepaid(amount);
		minted -= amount;
	}

	/**
	 * Force the sale of some collateral after the position is expired.
	 *
	 * Can only be called by the minting hub and the minting hub is trusted to calculate the price correctly.
	 * The proceeds from the sale are first used to repay the outstanding balance and then (if anything is left)
	 * it is sent to the owner of the position.
	 *
	 * Do not allow a forced sale as long as there is an open challenge. Otherwise, a forced sale by the owner
	 * himself could remove any incentive to launch challenges shortly before the expiration. (CS-ZCHF2-001)
	 */
	function forceSale(address buyer, uint256 collAmount, uint256 proceeds) external onlyHub expired noChallenge {
		// send collateral to buyer
		uint256 remainingCollateral = _sendCollateral(buyer, collAmount);
		if (minted > 0) {
			uint256 availableReserve = zchf.calculateAssignedReserve(minted, reserveContribution);
			if (proceeds + availableReserve >= minted) {
				// repay everything from the buyer's account
				uint256 returnedReserve = zchf.burnFromWithReserve(buyer, minted, reserveContribution);
				assert(returnedReserve == availableReserve);
				// transfer the remaining purchase price from the buyer to the owner
				zchf.transferFrom(buyer, owner, proceeds + returnedReserve - minted);
				_notifyRepaid(minted);
			} else {
				// we can only repay a part, nothing left to pay to owner
				zchf.transferFrom(buyer, address(this), proceeds);
				if (remainingCollateral == 0) {
					// CS-ZCHF2-002, bad debt should be properly handled. In this case, the proceeds from
					// the forced sale did not suffice to repay the position and there is a loss
					zchf.coverLoss(address(this), minted - proceeds); // more than we need, but returned again on next line
					zchf.burnWithoutReserve(minted, reserveContribution);
					_notifyRepaid(minted);
				} else {
					uint256 repaid = zchf.burnWithReserve(proceeds, reserveContribution);
					_notifyRepaid(repaid);
				}
			}
		} else {
			// wire funds directly to owner
			zchf.transferFrom(buyer, owner, proceeds);
		}
		emit MintingUpdate(_collateralBalance(), price, minted);
	}

	/**
	 * @notice Withdraw any ERC20 token that might have ended up on this address.
	 * Withdrawing collateral is subject to the same restrictions as withdrawCollateral(...).
	 */
	function withdraw(address token, address target, uint256 amount) external onlyOwner {
		if (token == address(collateral)) {
			withdrawCollateral(target, amount);
		} else {
			uint256 balance = _collateralBalance();
			IERC20(token).transfer(target, amount);
			require(balance == _collateralBalance()); // guard against double-entry-point tokens
		}
	}

	/**
	 * @notice Withdraw collateral from the position up to the extent that it is still well collateralized afterwards.
	 * Not possible as long as there is an open challenge or the contract is subject to a cooldown.
	 *
	 * Withdrawing collateral below the minimum collateral amount formally closes the position.
	 */
	function withdrawCollateral(address target, uint256 amount) public ownerOrRoller {
		uint256 balance = _withdrawCollateral(target, amount);
		emit MintingUpdate(balance, price, minted);
	}

	function _withdrawCollateral(address target, uint256 amount) internal noChallenge returns (uint256) {
		if (block.timestamp <= cooldown) revert Hot();
		uint256 balance = _sendCollateral(target, amount);
		_checkCollateral(balance, price);
		return balance;
	}

	function _sendCollateral(address target, uint256 amount) internal returns (uint256) {
		if (amount > 0) {
			// Some weird tokens fail when trying to transfer 0 amounts
			IERC20(collateral).transfer(target, amount);
		}
		uint256 balance = _collateralBalance();
		if (balance < minimumCollateral) {
			_close();
		}
		return balance;
	}

	/**
	 * @notice This invariant must always hold and must always be checked when any of the three
	 * variables change in an adverse way.
	 */
	function _checkCollateral(uint256 collateralReserve, uint256 atPrice) internal view {
		uint256 relevantCollateral = collateralReserve < minimumCollateral ? 0 : collateralReserve;
		if (relevantCollateral * atPrice < minted * ONE_DEC18)
			revert InsufficientCollateral(relevantCollateral * atPrice, minted * ONE_DEC18);
	}

	/**
	 * @notice Returns the liquidation price and the durations for phase1 and phase2 of the challenge.
	 * Both phases are usually of equal duration, but near expiration, phase one is adjusted such that
	 * it cannot last beyond the expiration date of the position.
	 */
	function challengeData() external view returns (uint256 liqPrice, uint40 phase) {
		return (price, challengePeriod);
	}

	function notifyChallengeStarted(uint256 size) external onlyHub alive {
		// Require minimum size. Collateral balance can be below minimum if it was partially challenged before.
		if (size < minimumCollateral && size < _collateralBalance()) revert ChallengeTooSmall();
		if (size == 0) revert ChallengeTooSmall();
		challengedAmount += size;
	}

	/**
	 * @param size   amount of collateral challenged (dec18)
	 */
	function notifyChallengeAverted(uint256 size) external onlyHub {
		challengedAmount -= size;

		// Don't allow minter to close the position immediately so challenge can be repeated before
		// the owner has a chance to mint more on an undercollateralized position
		_restrictMinting(1 days);
	}

	/**
	 * @notice Notifies the position that a challenge was successful.
	 * Triggers the payout of the challenged part of the collateral.
	 * Everything else is assumed to be handled by the hub.
	 *
	 * @param _bidder   address of the bidder that receives the collateral
	 * @param _size     amount of the collateral bid for
	 * @return (position owner, effective challenge size in ZCHF, amount to be repaid, reserve ppm)
	 */
	function notifyChallengeSucceeded(address _bidder, uint256 _size) external onlyHub returns (address, uint256, uint256, uint32) {
		challengedAmount -= _size;
		uint256 colBal = _collateralBalance();
		if (colBal < _size) {
			_size = colBal;
		}
		uint256 repayment = colBal == 0 ? 0 : (minted * _size) / colBal; // for enormous colBal, this could be rounded to 0, which is ok
		_notifyRepaid(repayment); // we assume the caller takes care of the actual repayment

		// Give time for additional challenges before the owner can mint again. In particular,
		// the owner might have added collateral only seconds before the challenge ended, preventing a close.
		_restrictMinting(3 days);

		uint256 newBalance = _sendCollateral(_bidder, _size); // transfer collateral to the bidder and emit update

		emit MintingUpdate(newBalance, price, minted);

		return (owner, _size, repayment, reserveContribution);
	}
}

interface IHub {
	function rate() external view returns (ILeadrate);

	function roller() external view returns (address);
}

File 2 of 8: IERC20.sol
/**
 * SPDX-License-Identifier: MIT
 *
 * Copyright (c) 2016-2019 zOS Global Limited
 *
 */
pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see `ERC20Detailed`.
 */

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

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

    function decimals() external view returns (uint8);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns always true. Throws error on failure.
     *
     * Emits a `Transfer` event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through `transferFrom`. This is
     * zero by default.
     *
     * This value can change when `approve` or `transferFrom` are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * > Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an `Approval` event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns always true. Throws error on failure.
     *
     * Emits a `Transfer` event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to `approve`. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 3 of 8: IFrankencoin.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./IReserve.sol";

interface IFrankencoin is IERC20 {
    function suggestMinter(address _minter, uint256 _applicationPeriod, uint256 _applicationFee, string calldata _message) external;

    function registerPosition(address position) external;

    function denyMinter(address minter, address[] calldata helpers, string calldata message) external;

    function reserve() external view returns (IReserve);

    function minterReserve() external view returns (uint256);

    function calculateAssignedReserve(uint256 mintedAmount, uint32 _reservePPM) external view returns (uint256);

    function calculateFreedAmount(uint256 amountExcludingReserve, uint32 reservePPM) external view returns (uint256);

    function equity() external view returns (uint256);

    function isMinter(address minter) external view returns (bool);

    function getPositionParent(address position) external view returns (address);

    function mint(address target, uint256 amount) external;

    function mintWithReserve(address target, uint256 amount, uint32 reservePPM, uint32 feePPM) external;

    function burnFrom(address target, uint256 amount) external;

    function burnWithoutReserve(uint256 amountIncludingReserve, uint32 reservePPM) external;

    function burnFromWithReserve(address payer, uint256 targetTotalBurnAmount, uint32 _reservePPM) external returns (uint256);

    function burnWithReserve(uint256 amountExcludingReserve, uint32 reservePPM) external returns (uint256);

    function coverLoss(address source, uint256 amount) external;

    function collectProfits(address source, uint256 _amount) external;
}

File 4 of 8: ILeadrate.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ILeadrate {
   function currentRatePPM() external view returns (uint24);
   function currentTicks() external view returns (uint64);
}

File 5 of 8: IPosition.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import './IERC20.sol';

interface IPosition {
	function hub() external view returns (address);

	function collateral() external view returns (IERC20);

	function minimumCollateral() external view returns (uint256);

	function price() external view returns (uint256);

	function minted() external view returns (uint256);

	function challengedAmount() external view returns (uint256);

	function original() external view returns (address);

	function expiration() external view returns (uint40);

	function cooldown() external view returns (uint40);

	function limit() external view returns (uint256);

	function challengePeriod() external view returns (uint40);

	function start() external view returns (uint40);

	function riskPremiumPPM() external view returns (uint24);

	function reserveContribution() external view returns (uint24);

	function initialize(address parent, uint40 _expiration) external;

	function assertCloneable() external;

	function notifyMint(uint256 mint_) external;

	function notifyRepaid(uint256 repaid_) external;

	function availableForClones() external view returns (uint256);

	function availableForMinting() external view returns (uint256);

	function deny(address[] calldata helpers, string calldata message) external;

	function getUsableMint(uint256 totalMint, bool afterFees) external view returns (uint256);

	function getMintAmount(uint256 usableMint) external view returns (uint256);

	function adjust(uint256 newMinted, uint256 newCollateral, uint256 newPrice) external;

	function adjustPrice(uint256 newPrice) external;

	function mint(address target, uint256 amount) external;

	function calculateCurrentFee() external view returns (uint24);

	function annualInterestPPM() external view returns (uint24);

	function calculateFee(uint256 exp) external view returns (uint24);

	function repay(uint256 amount) external returns (uint256);

	function forceSale(address buyer, uint256 collAmount, uint256 proceeds) external;

	function withdraw(address token, address target, uint256 amount) external;

	function withdrawCollateral(address target, uint256 amount) external;

	function challengeData() external view returns (uint256 liqPrice, uint40 phase);

	function notifyChallengeStarted(uint256 size) external;

	function notifyChallengeAverted(uint256 size) external;

	function notifyChallengeSucceeded(address _bidder, uint256 _size) external returns (address, uint256, uint256, uint32);
}

File 6 of 8: IReserve.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IERC20.sol";

interface IReserve is IERC20 {
   function invest(uint256 amount, uint256 expected) external returns (uint256);
   function checkQualified(address sender, address[] calldata helpers) external view;
}

File 7 of 8: MathUtil.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title Functions for share valuation
 */
contract MathUtil {
    uint256 internal constant ONE_DEC18 = 10 ** 18;

    // Let's go for 12 digits of precision (18-6)
    uint256 internal constant THRESH_DEC18 = 10 ** 6;

    /**
     * @notice Cubic root with Halley approximation
     *         Number 1e18 decimal
     * @param _v     number for which we calculate x**(1/3)
     * @return returns _v**(1/3)
     */
    function _cubicRoot(uint256 _v) internal pure returns (uint256) {
        // Good first guess for _v slightly above 1.0, which is often the case in the Frankencoin system
        uint256 x = _v > ONE_DEC18 && _v < 10 ** 19 ? (_v - ONE_DEC18) / 3 + ONE_DEC18 : ONE_DEC18;
        uint256 diff;
        do {
            uint256 powX3 = _mulD18(_mulD18(x, x), x);
            uint256 xnew = x * (powX3 + 2 * _v) / (2 * powX3 + _v);
            diff = xnew > x ? xnew - x : x - xnew;
            x = xnew;
        } while (diff > THRESH_DEC18);
        return x;
    }

    function _mulD18(uint256 _a, uint256 _b) internal pure returns (uint256) {
        return (_a * _b) / ONE_DEC18;
    }

    function _divD18(uint256 _a, uint256 _b) internal pure returns (uint256) {
        return (_a * ONE_DEC18) / _b;
    }

    function _power3(uint256 _x) internal pure returns (uint256) {
        return _mulD18(_mulD18(_x, _x), _x);
    }

    function _min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }
}

File 8 of 8: Ownable.sol
// SPDX-License-Identifier: MIT
//
// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
//
// Modifications:
// - Replaced Context._msgSender() with msg.sender
// - Made leaner

pragma solidity ^0.8.0;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 */
contract Ownable {

    address public owner;

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

    error NotOwner();

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _setOwner(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _setOwner(address newOwner) internal {
        require(newOwner != address(0x0)); // ensure initialization can only done once
        address oldOwner = owner;
        owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    function _requireOwner(address sender) internal view {
        if (owner != sender && owner != address(0x0)) revert NotOwner();
    }

    modifier onlyOwner() {
        _requireOwner(msg.sender);
        _;
    }
}

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_hub","type":"address"},{"internalType":"address","name":"_zchf","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_minCollateral","type":"uint256"},{"internalType":"uint256","name":"_initialLimit","type":"uint256"},{"internalType":"uint40","name":"_initPeriod","type":"uint40"},{"internalType":"uint40","name":"_duration","type":"uint40"},{"internalType":"uint40","name":"_challengePeriod","type":"uint40"},{"internalType":"uint24","name":"_riskPremiumPPM","type":"uint24"},{"internalType":"uint256","name":"_liqPrice","type":"uint256"},{"internalType":"uint24","name":"_reservePPM","type":"uint24"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Alive","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ChallengeTooSmall","type":"error"},{"inputs":[],"name":"Challenged","type":"error"},{"inputs":[],"name":"Closed","type":"error"},{"inputs":[{"internalType":"uint40","name":"time","type":"uint40"},{"internalType":"uint40","name":"expiration","type":"uint40"}],"name":"Expired","type":"error"},{"inputs":[],"name":"Hot","type":"error"},{"inputs":[{"internalType":"uint256","name":"needed","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"InsufficientCollateral","type":"error"},{"inputs":[],"name":"InvalidExpiration","type":"error"},{"inputs":[{"internalType":"uint256","name":"tried","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"LimitExceeded","type":"error"},{"inputs":[],"name":"NotHub","type":"error"},{"inputs":[],"name":"NotOriginal","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[{"internalType":"uint256","name":"excess","type":"uint256"}],"name":"RepaidTooMuch","type":"error"},{"inputs":[],"name":"TooLate","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minted","type":"uint256"}],"name":"MintingUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"string","name":"message","type":"string"}],"name":"PositionDenied","type":"event"},{"inputs":[{"internalType":"uint256","name":"newMinted","type":"uint256"},{"internalType":"uint256","name":"newCollateral","type":"uint256"},{"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"adjust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"adjustPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"annualInterestPPM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"assertCloneable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"availableForClones","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"availableForMinting","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"calculateCurrentFee","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"exp","type":"uint256"}],"name":"calculateFee","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"challengeData","outputs":[{"internalType":"uint256","name":"liqPrice","type":"uint256"},{"internalType":"uint40","name":"phase","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"challengePeriod","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"challengedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cooldown","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"helpers","type":"address[]"},{"internalType":"string","name":"message","type":"string"}],"name":"deny","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"expiration","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint256","name":"collAmount","type":"uint256"},{"internalType":"uint256","name":"proceeds","type":"uint256"}],"name":"forceSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"usableMint","type":"uint256"}],"name":"getMintAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"totalMint","type":"uint256"},{"internalType":"bool","name":"afterFees","type":"bool"}],"name":"getUsableMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hub","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"parent","type":"address"},{"internalType":"uint40","name":"_expiration","type":"uint40"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isClosed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"limit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"size","type":"uint256"}],"name":"notifyChallengeAverted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"size","type":"uint256"}],"name":"notifyChallengeStarted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_bidder","type":"address"},{"internalType":"uint256","name":"_size","type":"uint256"}],"name":"notifyChallengeSucceeded","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mint_","type":"uint256"}],"name":"notifyMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"repaid_","type":"uint256"}],"name":"notifyRepaid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"original","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"repay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveContribution","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"riskPremiumPPM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"start","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"zchf","outputs":[{"internalType":"contract IFrankencoin","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.