Contract Overview
Balance:
0 ETH
ETH Value:
$0.00
My Name Tag:
Not Available
Txn Hash | Method |
Block
|
From
|
To
|
Value | [Txn Fee] | |||
---|---|---|---|---|---|---|---|---|---|
0xa2af1618c654ae19819795d506675ddf9249d0f2be02f9df4f5ae5281db22985 | 0x61016060 | 76023789 | 172 days 13 hrs ago | 0xd322a9876222dea06a478d4a69b75cb83b81eb3c | IN | Create: BalancedVault | 0 ETH | 0.00194196 |
[ Download CSV Export ]
Contract Name:
BalancedVault
Compiler Version
v0.8.17+commit.8df45f5f
Contract Source Code (Solidity Standard Json-Input format)
//SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/UFixed18.sol"; import "@equilibria/root/token/types/Token18.sol"; import "@equilibria/root/token/types/Token6.sol"; import "../interfaces/IEmptySetReserve.sol"; interface IBatcher { event Wrap(address indexed to, UFixed18 amount); event Unwrap(address indexed to, UFixed18 amount); event Rebalance(UFixed18 newMinted, UFixed18 newRedeemed); event Close(UFixed18 amount); error BatcherNotImplementedError(); error BatcherBalanceMismatchError(UFixed18 oldBalance, UFixed18 newBalance); function RESERVE() external view returns (IEmptySetReserve); // solhint-disable-line func-name-mixedcase function USDC() external view returns (Token6); // solhint-disable-line func-name-mixedcase function DSU() external view returns (Token18); // solhint-disable-line func-name-mixedcase function totalBalance() external view returns (UFixed18); function wrap(UFixed18 amount, address to) external; function unwrap(UFixed18 amount, address to) external; function rebalance() external; }
//SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/UFixed18.sol"; interface IEmptySetReserve { event Redeem(address indexed account, uint256 costAmount, uint256 redeemAmount); event Mint(address indexed account, uint256 mintAmount, uint256 costAmount); event Repay(address indexed account, uint256 repayAmount); function debt(address borrower) external view returns (UFixed18); function repay(address borrower, UFixed18 amount) external; function mint(UFixed18 amount) external; function redeem(UFixed18 amount) external; }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/Fixed18.sol"; interface IOracleProvider { /// @dev Error for invalid oracle round error InvalidOracleRound(); /// @dev A singular oracle version with its corresponding data struct OracleVersion { /// @dev The iterative version uint256 version; /// @dev the timestamp of the oracle update uint256 timestamp; /// @dev The oracle price of the corresponding version Fixed18 price; } function sync() external returns (OracleVersion memory); function currentVersion() external view returns (OracleVersion memory); function atVersion(uint256 oracleVersion) external view returns (OracleVersion memory); }
//SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.17; import "./interfaces/IBalancedVault.sol"; import "@equilibria/root/control/unstructured/UInitializable.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; /** * @title BalancedVault * @notice ERC4626 vault that manages a 50-50 position between long-short markets of the same payoff on Perennial. * @dev Vault deploys and rebalances collateral between the corresponding long and short markets, while attempting to * maintain `targetLeverage` with its open positions at any given time. Deposits are only gated in so much as to cap * the maximum amount of assets in the vault. * * The vault has a "delayed mint" mechanism for shares on deposit. After depositing to the vault, a user must wait * until the next settlement of the underlying products in order for shares to be reflected in the getters. * The shares will be fully reflected in contract state when the next settlement occurs on the vault itself. * Similarly, when redeeming shares, underlying assets are not claimable until a settlement occurs. * Each state changing interaction triggers the `settle` flywheel in order to bring the vault to the * desired state. * In the event that there is not a settlement for a long period of time, keepers can call the `sync` method to * force settlement and rebalancing. This is most useful to prevent vault liquidation due to PnL changes * causing the vault to be in an unhealthy state (far away from target leverage) */ contract BalancedVault is IBalancedVault, UInitializable { UFixed18 constant private TWO = UFixed18.wrap(2e18); /// @dev The address of the Perennial controller contract IController public immutable controller; /// @dev The address of the Perennial collateral contract ICollateral public immutable collateral; /// @dev The address of the Perennial product on the long side IProduct public immutable long; /// @dev The address of the Perennial product on the short side IProduct public immutable short; /// @dev The target leverage amount for the vault UFixed18 public immutable targetLeverage; /// @dev The collateral cap for the vault UFixed18 public immutable maxCollateral; /// @dev The underlying asset of the vault Token18 public immutable asset; /// @dev The ERC20 name of the vault string public name; /// @dev The ERC20 symbol of the vault string public symbol; /// @dev Mapping of allowance across all users mapping(address => mapping(address => UFixed18)) public allowance; /// @dev Mapping of shares of the vault per user mapping(address => UFixed18) private _balanceOf; /// @dev Total number of shares across all users UFixed18 private _totalSupply; /// @dev Mapping of unclaimed underlying of the vault per user mapping(address => UFixed18) private _unclaimed; /// @dev Mapping of unclaimed underlying of the vault per user UFixed18 private _totalUnclaimed; /// @dev Deposits that have not been settled, or have been settled but not yet processed by this contract UFixed18 private _deposit; /// @dev Redemptions that have not been settled, or have been settled but not yet processed by this contract UFixed18 private _redemption; /// @dev The latest version that a pending deposit or redemption has been placed uint256 private _latestVersion; /// @dev Mapping of pending (not yet converted to shares) per user mapping(address => UFixed18) private _deposits; /// @dev Mapping of pending (not yet withdrawn) per user mapping(address => UFixed18) private _redemptions; /// @dev Mapping of the latest version that a pending deposit or redemption has been placed per user mapping(address => uint256) private _latestVersions; /// @dev Mapping of versions of the vault state at a given oracle version mapping(uint256 => Version) private _versions; constructor( Token18 asset_, IController controller_, IProduct long_, IProduct short_, UFixed18 targetLeverage_, UFixed18 maxCollateral_ ) { asset = asset_; controller = controller_; collateral = controller_.collateral(); long = long_; short = short_; targetLeverage = targetLeverage_; maxCollateral = maxCollateral_; } /** * @notice Initializes the contract state * @param name_ ERC20 asset name * @param symbol_ ERC20 asset symbol */ function initialize(string memory name_, string memory symbol_) external initializer(1) { name = name_; symbol = symbol_; asset.approve(address(collateral)); } /** * @notice Rebalances the collateral and position of the vault without a deposit or withdraw * @dev Should be called by a keeper when the vault approaches a liquidation state on either side */ function sync() external { (VersionContext memory context, ) = _settle(address(0)); _rebalance(context, UFixed18Lib.ZERO); } /** * @notice Deposits `assets` assets into the vault, returning shares to `account` after the deposit settles. * @param assets The amount of assets to deposit * @param account The account to deposit on behalf of */ function deposit(UFixed18 assets, address account) external { (VersionContext memory context, ) = _settle(account); if (assets.gt(_maxDepositAtVersion(context))) revert BalancedVaultDepositLimitExceeded(); _deposit = _deposit.add(assets); _latestVersion = context.version; _deposits[account] = _deposits[account].add(assets); _latestVersions[account] = context.version; emit Deposit(msg.sender, account, context.version, assets); asset.pull(msg.sender, assets); _rebalance(context, UFixed18Lib.ZERO); } /** * @notice Redeems `shares` shares from the vault * @dev Does not return any assets to the user due to delayed settlement. Use `claim` to claim assets * If account is not msg.sender, requires prior spending approval * @param shares The amount of shares to redeem * @param account The account to redeem on behalf of */ function redeem(UFixed18 shares, address account) external { if (msg.sender != account) _consumeAllowance(account, msg.sender, shares); (VersionContext memory context, VersionContext memory accountContext) = _settle(account); if (shares.gt(_maxRedeemAtVersion(context, accountContext, account))) revert BalancedVaultRedemptionLimitExceeded(); _redemption = _redemption.add(shares); _latestVersion = context.version; _redemptions[account] = _redemptions[account].add(shares); _latestVersions[account] = context.version; emit Redemption(msg.sender, account, context.version, shares); _burn(account, shares); _rebalance(context, UFixed18Lib.ZERO); } /** * @notice Claims all claimable assets for account, sending assets to account * @param account The account to claim for */ function claim(address account) external { (VersionContext memory context, ) = _settle(account); UFixed18 unclaimedAmount = _unclaimed[account]; UFixed18 unclaimedTotal = _totalUnclaimed; _unclaimed[account] = UFixed18Lib.ZERO; _totalUnclaimed = unclaimedTotal.sub(unclaimedAmount); emit Claim(msg.sender, account, unclaimedAmount); // pro-rate if vault has less collateral than unclaimed UFixed18 claimAmount = unclaimedAmount; (UFixed18 longCollateral, UFixed18 shortCollateral, UFixed18 idleCollateral) = _collateral(); UFixed18 totalCollateral = longCollateral.add(shortCollateral).add(idleCollateral); if (totalCollateral.lt(unclaimedTotal)) claimAmount = claimAmount.muldiv(totalCollateral, unclaimedTotal); _rebalance(context, claimAmount); asset.push(account, claimAmount); } /** * @notice Sets `amount` as the allowance of `spender` over the caller's shares * @param spender Address which can spend operate on shares * @param amount Amount of shares that spender can operate on * @return bool true if the approval was successful, otherwise reverts */ function approve(address spender, UFixed18 amount) external returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } /** * @notice Moves `amount` shares from the caller's account to `to` * @param to Address to send shares to * @param amount Amount of shares to send * @return bool true if the transfer was successful, otherwise reverts */ function transfer(address to, UFixed18 amount) external returns (bool) { _settle(msg.sender); _transfer(msg.sender, to, amount); return true; } /** * @notice Moves `amount` shares from `from to `to` * @param from Address to send shares from * @param to Address to send shares to * @param amount Amount of shares to send * @return bool true if the transfer was successful, otherwise reverts */ function transferFrom(address from, address to, UFixed18 amount) external returns (bool) { _settle(from); _consumeAllowance(from, msg.sender, amount); _transfer(from, to, amount); return true; } /** * @notice Returns the decimals places of the share token * @return Decimal places of the share share token */ function decimals() external pure returns (uint8) { return 18; } /** * @notice The maximum available deposit amount * @dev Only exact when vault is synced, otherwise approximate * @return Maximum available deposit amount */ function maxDeposit(address) external view returns (UFixed18) { (VersionContext memory context, ) = _loadContextForRead(address(0)); return _maxDepositAtVersion(context); } /** * @notice The maximum available redeemable amount * @dev Only exact when vault is synced, otherwise approximate * @param account The account to redeem for * @return Maximum available redeemable amount */ function maxRedeem(address account) external view returns (UFixed18) { (VersionContext memory context, VersionContext memory accountContext) = _loadContextForRead(account); return _maxRedeemAtVersion(context, accountContext, account); } /** * @notice The total amount of assets currently held by the vault * @return Amount of assets held by the vault */ function totalAssets() external view returns (UFixed18) { (VersionContext memory context, ) = _loadContextForRead(address(0)); return _totalAssetsAtVersion(context); } /** * @notice The total amount of shares currently issued * @return Amount of shares currently issued */ function totalSupply() external view returns (UFixed18) { (VersionContext memory context, ) = _loadContextForRead(address(0)); return _totalSupplyAtVersion(context); } /** * @notice Number of shares held by `account` * @param account Account to query balance of * @return Number of shares held by `account` */ function balanceOf(address account) external view returns (UFixed18) { (, VersionContext memory accountContext) = _loadContextForRead(account); return _balanceOfAtVersion(accountContext, account); } /** * @notice Total unclaimed assets in vault * @return Total unclaimed assets in vault */ function totalUnclaimed() external view returns (UFixed18) { (VersionContext memory context, ) = _loadContextForRead(address(0)); return _totalUnclaimedAtVersion(context); } /** * @notice `account`'s unclaimed assets * @param account Account to query unclaimed balance of * @return `account`'s unclaimed assets */ function unclaimed(address account) external view returns (UFixed18) { (, VersionContext memory accountContext) = _loadContextForRead(account); return _unclaimedAtVersion(accountContext, account); } /** * @notice Converts a given amount of assets to shares * @param assets Number of assets to convert to shares * @return Amount of shares for the given assets */ function convertToShares(UFixed18 assets) external view returns (UFixed18) { (VersionContext memory context, ) = _loadContextForRead(address(0)); (context.latestCollateral, context.latestShares) = (_totalAssetsAtVersion(context), _totalSupplyAtVersion(context)); return _convertToSharesAtVersion(context, assets); } /** * @notice Converts a given amount of shares to assets * @param shares Number of shares to convert to assets * @return Amount of assets for the given shares */ function convertToAssets(UFixed18 shares) external view returns (UFixed18) { (VersionContext memory context, ) = _loadContextForRead(address(0)); (context.latestCollateral, context.latestShares) = (_totalAssetsAtVersion(context), _totalSupplyAtVersion(context)); return _convertToAssetsAtVersion(context, shares); } /** * @notice Hook that is called before every stateful operation * @dev Settles the vault's account on both the long and short product, along with any global or user-specific deposits/redemptions * @param account The account that called the operation, or 0 if called by a keeper. * @return context The current version context */ function _settle(address account) private returns (VersionContext memory context, VersionContext memory accountContext) { (context, accountContext) = _loadContextForWrite(account); if (context.version > _latestVersion) { _delayedMint(_totalSupplyAtVersion(context).sub(_totalSupply)); _totalUnclaimed = _totalUnclaimedAtVersion(context); _deposit = UFixed18Lib.ZERO; _redemption = UFixed18Lib.ZERO; _latestVersion = context.version; _versions[context.version] = Version({ longPosition: long.position(address(this)).maker, shortPosition: short.position(address(this)).maker, totalShares: _totalSupply, longAssets: collateral.collateral(address(this), long), shortAssets: collateral.collateral(address(this), short), totalAssets: _totalAssetsAtVersion(context) }); } if (account != address(0) && accountContext.version > _latestVersions[account]) { _delayedMintAccount(account, _balanceOfAtVersion(accountContext, account).sub(_balanceOf[account])); _unclaimed[account] = _unclaimedAtVersion(accountContext, account); _deposits[account] = UFixed18Lib.ZERO; _redemptions[account] = UFixed18Lib.ZERO; _latestVersions[account] = accountContext.version; } } /** * @notice Rebalances the collateral and position of the vault * @dev Rebalance is executed on best-effort, any failing legs of the strategy will not cause a revert * @param claimAmount The amount of assets that will be withdrawn from the vault at the end of the operation */ function _rebalance(VersionContext memory context, UFixed18 claimAmount) private { _rebalanceCollateral(claimAmount); _rebalancePosition(context, claimAmount); } /** * @notice Rebalances the collateral of the vault * @param claimAmount The amount of assets that will be withdrawn from the vault at the end of the operation */ function _rebalanceCollateral(UFixed18 claimAmount) private { (UFixed18 longCollateral, UFixed18 shortCollateral, UFixed18 idleCollateral) = _collateral(); UFixed18 currentCollateral = longCollateral.add(shortCollateral).add(idleCollateral).sub(claimAmount); UFixed18 targetCollateral = currentCollateral.div(TWO); if (targetCollateral.lt(controller.minCollateral())) targetCollateral = UFixed18Lib.ZERO; (IProduct greaterProduct, IProduct lesserProduct) = longCollateral.gt(shortCollateral) ? (long, short) : (short, long); _updateCollateral(greaterProduct, greaterProduct == long ? longCollateral : shortCollateral, targetCollateral); _updateCollateral(lesserProduct, lesserProduct == long ? longCollateral : shortCollateral, targetCollateral); } /** * @notice Rebalances the position of the vault */ function _rebalancePosition(VersionContext memory context, UFixed18 claimAmount) private { UFixed18 currentAssets = _totalAssetsAtVersion(context).sub(claimAmount); UFixed18 currentUtilized = _totalSupply.add(_redemption).isZero() ? _deposit.add(currentAssets) : _deposit.add(currentAssets.muldiv(_totalSupply, _totalSupply.add(_redemption))); if (currentUtilized.lt(controller.minCollateral().mul(TWO))) currentUtilized = UFixed18Lib.ZERO; UFixed18 currentPrice = long.atVersion(context.version).price.abs(); UFixed18 targetPosition = currentUtilized.mul(targetLeverage).div(currentPrice).div(TWO); _updateMakerPosition(long, targetPosition); _updateMakerPosition(short, targetPosition); } /** * @notice Adjusts the collateral on `product` to `targetCollateral` * @param product The product to adjust the vault's collateral on * @param currentCollateral The current collateral of the product * @param targetCollateral The new collateral to target */ function _updateCollateral(IProduct product, UFixed18 currentCollateral, UFixed18 targetCollateral) private { if (currentCollateral.gt(targetCollateral)) collateral.withdrawTo(address(this), product, currentCollateral.sub(targetCollateral)); if (currentCollateral.lt(targetCollateral)) collateral.depositTo(address(this), product, targetCollateral.sub(currentCollateral)); emit CollateralUpdated(product, targetCollateral); } /** * @notice Adjusts the position on `product` to `targetPosition` * @param product The product to adjust the vault's position on * @param targetPosition The new position to target */ function _updateMakerPosition(IProduct product, UFixed18 targetPosition) private { UFixed18 currentPosition = product.position(address(this)).next(product.pre(address(this))).maker; UFixed18 currentMaker = product.positionAtVersion(product.latestVersion()).next(product.pre()).maker; UFixed18 makerLimit = product.makerLimit(); UFixed18 makerAvailable = makerLimit.gt(currentMaker) ? makerLimit.sub(currentMaker) : UFixed18Lib.ZERO; if (targetPosition.lt(currentPosition)) product.closeMake(currentPosition.sub(targetPosition)); if (targetPosition.gt(currentPosition)) product.openMake(targetPosition.sub(currentPosition).min(makerAvailable)); emit PositionUpdated(product, targetPosition); } /** * @notice Moves `amount` shares from `from` to `to` * @param from Address to send shares from * @param to Address to send shares to * @param amount Amount of shares to move */ function _transfer(address from, address to, UFixed18 amount) private { _balanceOf[from] = _balanceOf[from].sub(amount); _balanceOf[to] = _balanceOf[to].add(amount); emit Transfer(from, to, amount); } /** * @notice Burns `amount` shares from `from`, adjusting totalSupply * @param from Address to burn shares from * @param amount Amount of shares to burn */ function _burn(address from, UFixed18 amount) private { _balanceOf[from] = _balanceOf[from].sub(amount); _totalSupply = _totalSupply.sub(amount); emit Transfer(from, address(0), amount); } /** * @notice Mints `amount` shares, adjusting totalSupply * @param amount Amount of shares to mint */ function _delayedMint(UFixed18 amount) private { _totalSupply = _totalSupply.add(amount); } /** * @notice Mints `amount` shares to `to` * @param to Address to mint shares to * @param amount Amount of shares to mint */ function _delayedMintAccount(address to, UFixed18 amount) private { _balanceOf[to] = _balanceOf[to].add(amount); emit Transfer(address(0), to, amount); } /** * @notice Decrements `spender`s allowance for `account` by `amount` * @dev Does not decrement if approval is for -1 * @param account Address of allower * @param spender Address of spender * @param amount Amount to decrease allowance by */ function _consumeAllowance(address account, address spender, UFixed18 amount) private { if (allowance[account][spender].eq(UFixed18Lib.MAX)) return; allowance[account][spender] = allowance[account][spender].sub(amount); } /** * @notice Loads the context for the given `account`, settling the vault first * @param account Account to load the context for * @return global version context * @return account version context */ function _loadContextForWrite(address account) private returns (VersionContext memory, VersionContext memory) { long.settleAccount(address(this)); short.settleAccount(address(this)); uint256 currentVersion = long.latestVersion(address(this)); return ( VersionContext(currentVersion, _assetsAt(_latestVersion), _sharesAt(_latestVersion)), VersionContext(currentVersion, _assetsAt(_latestVersions[account]), _sharesAt(_latestVersions[account])) ); } /** * @notice Loads the context for the given `account` * @param account Account to load the context for * @return global version context * @return account version context */ function _loadContextForRead(address account) private view returns (VersionContext memory, VersionContext memory) { uint256 currentVersion = Math.min(long.latestVersion(), short.latestVersion()); // latest version that both products are settled to return ( VersionContext(currentVersion, _assetsAt(_latestVersion), _sharesAt(_latestVersion)), VersionContext(currentVersion, _assetsAt(_latestVersions[account]), _sharesAt(_latestVersions[account])) ); } /** * @notice Calculates whether or not the vault is in an unhealthy state at the provided version * @param context Version context to calculate health * @return bool true if unhealthy, false if healthy */ function _unhealthyAtVersion(VersionContext memory context) private view returns (bool) { return collateral.liquidatable(address(this), long) || collateral.liquidatable(address(this), short) || long.isLiquidating(address(this)) || short.isLiquidating(address(this)) || (!context.latestShares.isZero() && context.latestCollateral.isZero()); } /** * @notice The maximum available deposit amount at the given version * @param context Version context to use in calculation * @return Maximum available deposit amount at version */ function _maxDepositAtVersion(VersionContext memory context) private view returns (UFixed18) { if (_unhealthyAtVersion(context)) return UFixed18Lib.ZERO; UFixed18 currentCollateral = _totalAssetsAtVersion(context).add(_deposit); return maxCollateral.gt(currentCollateral) ? maxCollateral.sub(currentCollateral) : UFixed18Lib.ZERO; } /** * @notice The maximum available redeemable amount at the given version for `account` * @param context Version context to use in calculation * @param accountContext Account version context to use in calculation * @param account Account to calculate redeemable amount * @return Maximum available redeemable amount at version */ function _maxRedeemAtVersion( VersionContext memory context, VersionContext memory accountContext, address account ) private view returns (UFixed18) { if (_unhealthyAtVersion(context)) return UFixed18Lib.ZERO; return _balanceOfAtVersion(accountContext, account); } /** * @notice The total assets at the given version * @param context Version context to use in calculation * @return Total assets amount at version */ function _totalAssetsAtVersion(VersionContext memory context) private view returns (UFixed18) { (UFixed18 longCollateral, UFixed18 shortCollateral, UFixed18 idleCollateral) = _collateral(); (UFixed18 totalCollateral, UFixed18 totalDebt) = (longCollateral.add(shortCollateral).add(idleCollateral), _totalUnclaimedAtVersion(context).add(_deposit)); return totalCollateral.gt(totalDebt) ? totalCollateral.sub(totalDebt) : UFixed18Lib.ZERO; } /** * @notice The total supply at the given version * @param context Version context to use in calculation * @return Total supply amount at version */ function _totalSupplyAtVersion(VersionContext memory context) private view returns (UFixed18) { if (context.version == _latestVersion) return _totalSupply; return _totalSupply.add(_convertToSharesAtVersion(context, _deposit)); } /** * @notice The balance of `account` at the given version * @param accountContext Account version context to use in calculation * @param account Account to calculate balance of amount * @return Account balance at version */ function _balanceOfAtVersion(VersionContext memory accountContext, address account) private view returns (UFixed18) { if (accountContext.version == _latestVersions[account]) return _balanceOf[account]; return _balanceOf[account].add(_convertToSharesAtVersion(accountContext, _deposits[account])); } /** * @notice The total unclaimed assets at the given version * @param context Version context to use in calculation * @return Total unclaimed asset amount at version */ function _totalUnclaimedAtVersion(VersionContext memory context) private view returns (UFixed18) { if (context.version == _latestVersion) return _totalUnclaimed; return _totalUnclaimed.add(_convertToAssetsAtVersion(context, _redemption)); } /** * @notice The total unclaimed assets at the given version for `account` * @param accountContext Account version context to use in calculation * @param account Account to calculate unclaimed assets for * @return Total unclaimed asset amount for `account` at version */ function _unclaimedAtVersion(VersionContext memory accountContext, address account) private view returns (UFixed18) { if (accountContext.version == _latestVersions[account]) return _unclaimed[account]; return _unclaimed[account].add(_convertToAssetsAtVersion(accountContext, _redemptions[account])); } /** * @notice Returns the amounts of the individual sources of assets in the vault * @return The amount of collateral in the long product * @return The amount of collateral in the short product * @return The amount of collateral idle in the vault contract */ function _collateral() private view returns (UFixed18, UFixed18, UFixed18) { return ( collateral.collateral(address(this), long), collateral.collateral(address(this), short), asset.balanceOf() ); } /** * @notice The total assets at the given version * @dev Calculates and adds accumulated PnL for `version` + 1 * @param version Version to get total assets at * @return Total assets in the vault at the given version */ function _assetsAt(uint256 version) private view returns (UFixed18) { Fixed18 longAccumulated = long.valueAtVersion(version + 1).maker.sub(long.valueAtVersion(version).maker) .mul(Fixed18Lib.from(_versions[version].longPosition)) .max(Fixed18Lib.from(_versions[version].longAssets).mul(Fixed18Lib.NEG_ONE)); // collateral can't go negative on a product Fixed18 shortAccumulated = short.valueAtVersion(version + 1).maker.sub(short.valueAtVersion(version).maker) .mul(Fixed18Lib.from(_versions[version].shortPosition)) .max(Fixed18Lib.from(_versions[version].shortAssets).mul(Fixed18Lib.NEG_ONE)); // collateral can't go negative on a product return UFixed18Lib.from( Fixed18Lib.from(_versions[version].totalAssets) .add(longAccumulated) .add(shortAccumulated) .max(Fixed18Lib.ZERO) // vault can't have negative assets, socializes into unclaimed if triggered ); } /** * @notice The total shares at the given version * @param version Version to get total shares at * @return Total shares at `version` */ function _sharesAt(uint256 version) private view returns (UFixed18) { return _versions[version].totalShares; } /** * @notice Converts a given amount of assets to shares at version * @param context Version context to use in calculation * @param assets Number of assets to convert to shares * @return Amount of shares for the given assets at version */ function _convertToSharesAtVersion(VersionContext memory context, UFixed18 assets) private pure returns (UFixed18) { if (context.latestCollateral.isZero()) return assets; return assets.muldiv(context.latestShares, context.latestCollateral); } /** * @notice Converts a given amount of shares to assets at version * @param context Version context to use in calculation * @param shares Number of shares to convert to shares * @return Amount of assets for the given shares at version */ function _convertToAssetsAtVersion(VersionContext memory context, UFixed18 shares) private pure returns (UFixed18) { if (context.latestShares.isZero()) return shares; return shares.muldiv(context.latestCollateral, context.latestShares); } }
//SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/perennial/contracts/interfaces/IController.sol"; import "@equilibria/perennial/contracts/interfaces/ICollateral.sol"; import "@equilibria/root/number/types/UFixed18.sol"; interface IBalancedVault { /* BalancedVault Interface */ struct Version { UFixed18 longPosition; UFixed18 shortPosition; UFixed18 totalShares; UFixed18 longAssets; UFixed18 shortAssets; UFixed18 totalAssets; } struct VersionContext { uint256 version; UFixed18 latestCollateral; UFixed18 latestShares; } event Deposit(address indexed sender, address indexed account, uint256 version, UFixed18 assets); event Redemption(address indexed sender, address indexed account, uint256 version, UFixed18 shares); event Claim(address indexed sender, address indexed account, UFixed18 assets); event PositionUpdated(IProduct product, UFixed18 targetPosition); event CollateralUpdated(IProduct product, UFixed18 targetCollateral); error BalancedVaultDepositLimitExceeded(); error BalancedVaultRedemptionLimitExceeded(); function initialize(string memory name_, string memory symbol_) external; function sync() external; function controller() external view returns (IController); function collateral() external view returns (ICollateral); function long() external view returns (IProduct); function short() external view returns (IProduct); function targetLeverage() external view returns (UFixed18); function maxCollateral() external view returns (UFixed18); function unclaimed(address account) external view returns (UFixed18); function totalUnclaimed() external view returns (UFixed18); function claim(address account) external; /* Partial ERC4626 Interface */ function asset() external view returns (Token18); function totalAssets() external view returns (UFixed18); function convertToShares(UFixed18 assets) external view returns (UFixed18); function convertToAssets(UFixed18 shares) external view returns (UFixed18); function maxDeposit(address account) external view returns (UFixed18); function deposit(UFixed18 assets, address account) external; function maxRedeem(address account) external view returns (UFixed18); function redeem(UFixed18 shares, address account) external; /* Partial ERC20 Interface */ event Transfer(address indexed from, address indexed to, UFixed18 value); event Approval(address indexed account, address indexed spender, UFixed18 value); function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); function totalSupply() external view returns (UFixed18); function balanceOf(address account) external view returns (UFixed18); function transfer(address to, UFixed18 amount) external returns (bool); function allowance(address account, address spender) external view returns (UFixed18); function approve(address spender, UFixed18 amount) external returns (bool); function transferFrom(address from, address to, UFixed18 amount) external returns (bool); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/UFixed18.sol"; import "@equilibria/root/number/types/Fixed18.sol"; import "@equilibria/root/token/types/Token18.sol"; import "./IController.sol"; import "./IProduct.sol"; interface ICollateral { event Deposit(address indexed user, IProduct indexed product, UFixed18 amount); event Withdrawal(address indexed user, IProduct indexed product, UFixed18 amount); event AccountSettle(IProduct indexed product, address indexed account, Fixed18 amount, UFixed18 newShortfall); event ProductSettle(IProduct indexed product, UFixed18 protocolFee, UFixed18 productFee); event Liquidation(address indexed user, IProduct indexed product, address liquidator, UFixed18 fee); event ShortfallResolution(IProduct indexed product, UFixed18 amount); event FeeClaim(address indexed account, UFixed18 amount); error CollateralCantLiquidate(UFixed18 totalMaintenance, UFixed18 totalCollateral); error CollateralInsufficientCollateralError(); error CollateralUnderLimitError(); error CollateralZeroAddressError(); error CollateralAccountLiquidatingError(address account); function token() external view returns (Token18); function fees(address account) external view returns (UFixed18); function initialize(IController controller_) external; function depositTo(address account, IProduct product, UFixed18 amount) external; function withdrawTo(address receiver, IProduct product, UFixed18 amount) external; function withdrawFrom(address account, address receiver, IProduct product, UFixed18 amount) external; function liquidate(address account, IProduct product) external; function settleAccount(address account, Fixed18 amount) external; function settleProduct(UFixed18 amount) external; function collateral(address account, IProduct product) external view returns (UFixed18); function collateral(IProduct product) external view returns (UFixed18); function shortfall(IProduct product) external view returns (UFixed18); function liquidatable(address account, IProduct product) external view returns (bool); function liquidatableNext(address account, IProduct product) external view returns (bool); function resolveShortfall(IProduct product, UFixed18 amount) external; function claimFee() external; }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/Fixed18.sol"; interface IContractPayoffProvider { function payoff(Fixed18 price) external view returns (Fixed18 payoff); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/UFixed18.sol"; import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; import "./ICollateral.sol"; import "./IIncentivizer.sol"; import "./IProduct.sol"; import "./IMultiInvoker.sol"; import "./types/PayoffDefinition.sol"; interface IController { /// @dev Coordinator of a one or many products struct Coordinator { /// @dev Pending owner of the product, can accept ownership address pendingOwner; /// @dev Owner of the product, allowed to update select parameters address owner; /// @dev Treasury of the product, collects fees address treasury; } event CollateralUpdated(ICollateral newCollateral); event IncentivizerUpdated(IIncentivizer newIncentivizer); event ProductBeaconUpdated(IBeacon newProductBeacon); event MultiInvokerUpdated(IMultiInvoker newMultiInvoker); event ProtocolFeeUpdated(UFixed18 newProtocolFee); event MinFundingFeeUpdated(UFixed18 newMinFundingFee); event LiquidationFeeUpdated(UFixed18 newLiquidationFee); event IncentivizationFeeUpdated(UFixed18 newIncentivizationFee); event MinCollateralUpdated(UFixed18 newMinCollateral); event ProgramsPerProductUpdated(uint256 newProgramsPerProduct); event PauserUpdated(address newPauser); event PausedUpdated(bool newPaused); event CoordinatorPendingOwnerUpdated(uint256 indexed coordinatorId, address newPendingOwner); event CoordinatorOwnerUpdated(uint256 indexed coordinatorId, address newOwner); event CoordinatorTreasuryUpdated(uint256 indexed coordinatorId, address newTreasury); event CoordinatorCreated(uint256 indexed coordinatorId, address owner); event ProductCreated(IProduct indexed product, IProduct.ProductInfo productInfo); error ControllerNoZeroCoordinatorError(); error ControllerNotPauserError(); error ControllerNotOwnerError(uint256 controllerId); error ControllerNotPendingOwnerError(uint256 controllerId); error ControllerInvalidProtocolFeeError(); error ControllerInvalidMinFundingFeeError(); error ControllerInvalidLiquidationFeeError(); error ControllerInvalidIncentivizationFeeError(); error ControllerNotContractAddressError(); function collateral() external view returns (ICollateral); function incentivizer() external view returns (IIncentivizer); function productBeacon() external view returns (IBeacon); function multiInvoker() external view returns (IMultiInvoker); function coordinators(uint256 collateralId) external view returns (Coordinator memory); function coordinatorFor(IProduct product) external view returns (uint256); function protocolFee() external view returns (UFixed18); function minFundingFee() external view returns (UFixed18); function liquidationFee() external view returns (UFixed18); function incentivizationFee() external view returns (UFixed18); function minCollateral() external view returns (UFixed18); function programsPerProduct() external view returns (uint256); function pauser() external view returns (address); function paused() external view returns (bool); function initialize(ICollateral collateral_, IIncentivizer incentivizer_, IBeacon productBeacon_) external; function createCoordinator() external returns (uint256); function updateCoordinatorPendingOwner(uint256 coordinatorId, address newPendingOwner) external; function acceptCoordinatorOwner(uint256 coordinatorId) external; function updateCoordinatorTreasury(uint256 coordinatorId, address newTreasury) external; function createProduct(uint256 coordinatorId, IProduct.ProductInfo calldata productInfo) external returns (IProduct); function updateCollateral(ICollateral newCollateral) external; function updateIncentivizer(IIncentivizer newIncentivizer) external; function updateProductBeacon(IBeacon newProductBeacon) external; function updateMultiInvoker(IMultiInvoker newMultiInvoker) external; function updateProtocolFee(UFixed18 newProtocolFee) external; function updateMinFundingFee(UFixed18 newMinFundingFee) external; function updateLiquidationFee(UFixed18 newLiquidationFee) external; function updateIncentivizationFee(UFixed18 newIncentivizationFee) external; function updateMinCollateral(UFixed18 newMinCollateral) external; function updateProgramsPerProduct(uint256 newProductsPerProduct) external; function updatePauser(address newPauser) external; function updatePaused(bool newPaused) external; function isProduct(IProduct product) external view returns (bool); function owner() external view returns (address); function owner(uint256 coordinatorId) external view returns (address); function owner(IProduct product) external view returns (address); function treasury() external view returns (address); function treasury(uint256 coordinatorId) external view returns (address); function treasury(IProduct product) external view returns (address); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/token/types/Token18.sol"; import "@equilibria/root/number/types/UFixed18.sol"; import "@equilibria/perennial-oracle/contracts/interfaces/IOracleProvider.sol"; import "./types/ProgramInfo.sol"; import "./IController.sol"; import "./IProduct.sol"; interface IIncentivizer { event ProgramCreated(IProduct indexed product, uint256 indexed programId, ProgramInfo programInfo, UFixed18 programFeeAmount); event ProgramStarted(IProduct indexed product, uint256 indexed programId, uint256 version); event ProgramComplete(IProduct indexed product, uint256 indexed programId, uint256 version); event Claim(IProduct indexed product, address indexed account, uint256 indexed programId, UFixed18 amount); event FeeClaim(Token18 indexed token, UFixed18 amount); error IncentivizerNotAllowedError(IProduct product); error IncentivizerTooManyProgramsError(); error IncentivizerNotProgramOwnerError(IProduct product, uint256 programId); error IncentivizerInvalidProgramError(IProduct product, uint256 programId); error IncentivizerBatchClaimArgumentMismatchError(); function programInfos(IProduct product, uint256 programId) external view returns (ProgramInfo memory); function fees(Token18 token) external view returns (UFixed18); function initialize(IController controller_) external; function create(IProduct product, ProgramInfo calldata info) external returns (uint256); function complete(IProduct product, uint256 programId) external; function sync(IOracleProvider.OracleVersion memory currentOracleVersion) external; function syncAccount(address account, IOracleProvider.OracleVersion memory currentOracleVersion) external; function claim(IProduct product, uint256[] calldata programIds) external; function claimFor(address account, IProduct product, uint256[] calldata programIds) external; function claim(IProduct[] calldata products, uint256[][] calldata programIds) external; function claimFee(Token18[] calldata tokens) external; function active(IProduct product) external view returns (uint256); function count(IProduct product) external view returns (uint256); function unclaimed(IProduct product, address account, uint256 programId) external view returns (UFixed18); function available(IProduct product, uint256 programId) external view returns (UFixed18); function versionStarted(IProduct product, uint256 programId) external view returns (uint256); function versionComplete(IProduct product, uint256 programId) external view returns (uint256); function owner(IProduct product, uint256 programId) external view returns (address); function treasury(IProduct product, uint256 programId) external view returns (address); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/token/types/Token6.sol"; import "@equilibria/root/token/types/Token18.sol"; import "@equilibria/emptyset-batcher/interfaces/IBatcher.sol"; import "@equilibria/emptyset-batcher/interfaces/IEmptySetReserve.sol"; import "./IController.sol"; import "./ICollateral.sol"; import "./IProduct.sol"; interface IMultiInvoker { /// @dev Core protocol actions that can be composed enum PerennialAction { NO_OP, DEPOSIT, WITHDRAW, OPEN_TAKE, CLOSE_TAKE, OPEN_MAKE, CLOSE_MAKE, CLAIM, WRAP, UNWRAP, WRAP_AND_DEPOSIT, WITHDRAW_AND_UNWRAP } /// @dev Struct for action invocation struct Invocation { PerennialAction action; bytes args; } function initialize() external; function USDC() external view returns (Token6); // solhint-disable-line func-name-mixedcase function DSU() external view returns (Token18); // solhint-disable-line func-name-mixedcase function batcher() external view returns (IBatcher); function controller() external view returns (IController); function collateral() external view returns (ICollateral); function reserve() external view returns (IEmptySetReserve); function invoke(Invocation[] calldata invocations) external; }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/UFixed18.sol"; import "@equilibria/root/curve/types/JumpRateUtilizationCurve.sol"; import "./types/PendingFeeUpdates.sol"; interface IParamProvider { event MaintenanceUpdated(UFixed18 newMaintenance, uint256 version); event FundingFeeUpdated(UFixed18 newFundingFee, uint256 version); event MakerFeeUpdated(UFixed18 newMakerFee, uint256 version); event PendingMakerFeeUpdated(UFixed18 newMakerFee); event TakerFeeUpdated(UFixed18 newTakerFee, uint256 version); event PendingTakerFeeUpdated(UFixed18 newTakerFee); event PositionFeeUpdated(UFixed18 newPositionFee, uint256 version); event PendingPositionFeeUpdated(UFixed18 newPositionFee); event MakerLimitUpdated(UFixed18 newMakerLimit, uint256 version); event JumpRateUtilizationCurveUpdated( JumpRateUtilizationCurve, uint256 version ); error ParamProviderInvalidParamValue(); function maintenance() external view returns (UFixed18); function updateMaintenance(UFixed18 newMaintenance) external; function fundingFee() external view returns (UFixed18); function updateFundingFee(UFixed18 newFundingFee) external; function makerFee() external view returns (UFixed18); function updateMakerFee(UFixed18 newMakerFee) external; function takerFee() external view returns (UFixed18); function updateTakerFee(UFixed18 newTakerFee) external; function positionFee() external view returns (UFixed18); function updatePositionFee(UFixed18 newPositionFee) external; function makerLimit() external view returns (UFixed18); function updateMakerLimit(UFixed18 newMakerLimit) external; function utilizationCurve() external view returns (JumpRateUtilizationCurve memory); function updateUtilizationCurve(JumpRateUtilizationCurve memory newUtilizationCurve) external; function pendingFeeUpdates() external view returns (PendingFeeUpdates memory); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/Fixed18.sol"; import "@equilibria/perennial-oracle/contracts/interfaces/IOracleProvider.sol"; import "./types/PayoffDefinition.sol"; interface IPayoffProvider { event OracleUpdated(address newOracle, uint256 oracleVersion); error PayoffProviderInvalidOracle(); error PayoffProviderInvalidPayoffDefinitionError(); function oracle() external view returns (IOracleProvider); function payoffDefinition() external view returns (PayoffDefinition memory); function currentVersion() external view returns (IOracleProvider.OracleVersion memory); function atVersion(uint256 oracleVersion) external view returns (IOracleProvider.OracleVersion memory); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/UFixed18.sol"; import "@equilibria/root/curve/types/JumpRateUtilizationCurve.sol"; import "./IPayoffProvider.sol"; import "./IParamProvider.sol"; import "./types/PayoffDefinition.sol"; import "./types/Position.sol"; import "./types/PrePosition.sol"; import "./types/Accumulator.sol"; interface IProduct is IPayoffProvider, IParamProvider { /// @dev Product Creation parameters struct ProductInfo { /// @dev name of the product string name; /// @dev symbol of the product string symbol; /// @dev product payoff definition PayoffDefinition payoffDefinition; /// @dev oracle address IOracleProvider oracle; /// @dev product maintenance ratio UFixed18 maintenance; /// @dev product funding fee UFixed18 fundingFee; /// @dev product maker fee UFixed18 makerFee; /// @dev product taker fee UFixed18 takerFee; /// @dev product position fee share UFixed18 positionFee; /// @dev product maker limit UFixed18 makerLimit; /// @dev utulization curve definition JumpRateUtilizationCurve utilizationCurve; } event Settle(uint256 preVersion, uint256 toVersion); event AccountSettle(address indexed account, uint256 preVersion, uint256 toVersion); event MakeOpened(address indexed account, uint256 version, UFixed18 amount); event TakeOpened(address indexed account, uint256 version, UFixed18 amount); event MakeClosed(address indexed account, uint256 version, UFixed18 amount); event TakeClosed(address indexed account, uint256 version, UFixed18 amount); event ClosedUpdated(bool indexed newClosed, uint256 version); error ProductInsufficientLiquidityError(UFixed18 socializationFactor); error ProductDoubleSidedError(); error ProductOverClosedError(); error ProductInsufficientCollateralError(); error ProductInLiquidationError(); error ProductMakerOverLimitError(); error ProductOracleBootstrappingError(); error ProductClosedError(); function name() external view returns (string memory); function symbol() external view returns (string memory); function initialize(ProductInfo calldata productInfo_) external; function settle() external; function settleAccount(address account) external; function openTake(UFixed18 amount) external; function openTakeFor(address account, UFixed18 amount) external; function closeTake(UFixed18 amount) external; function closeTakeFor(address account, UFixed18 amount) external; function openMake(UFixed18 amount) external; function openMakeFor(address account, UFixed18 amount) external; function closeMake(UFixed18 amount) external; function closeMakeFor(address account, UFixed18 amount) external; function closeAll(address account) external; function maintenance(address account) external view returns (UFixed18); function maintenanceNext(address account) external view returns (UFixed18); function isClosed(address account) external view returns (bool); function isLiquidating(address account) external view returns (bool); function position(address account) external view returns (Position memory); function pre(address account) external view returns (PrePosition memory); function latestVersion() external view returns (uint256); function positionAtVersion(uint256 oracleVersion) external view returns (Position memory); function pre() external view returns (PrePosition memory); function valueAtVersion(uint256 oracleVersion) external view returns (Accumulator memory); function shareAtVersion(uint256 oracleVersion) external view returns (Accumulator memory); function latestVersion(address account) external view returns (uint256); function rate(Position memory position) external view returns (Fixed18); function closed() external view returns (bool); function updateClosed(bool newClosed) external; function updateOracle(IOracleProvider newOracle) external; }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/Fixed18.sol"; import "./PackedAccumulator.sol"; /// @dev Accumulator type struct Accumulator { /// @dev maker accumulator per share Fixed18 maker; /// @dev taker accumulator per share Fixed18 taker; } using AccumulatorLib for Accumulator global; /** * @title AccountAccumulatorLib * @notice Library that surfaces math operations for the Accumulator type. * @dev Accumulators track the cumulative change in position value over time for the maker and taker positions * respectively. Account-level accumulators can then use two of these values `a` and `a'` to compute the * change in position value since last sync. This change in value is then used to compute P&L and fees. */ library AccumulatorLib { /** * @notice Creates a packed accumulator from an accumulator * @param self an accumulator * @return New packed accumulator */ function pack(Accumulator memory self) internal pure returns (PackedAccumulator memory) { return PackedAccumulator({maker: self.maker.pack(), taker: self.taker.pack()}); } /** * @notice Adds two accumulators together * @param a The first accumulator to sum * @param b The second accumulator to sum * @return The resulting summed accumulator */ function add(Accumulator memory a, Accumulator memory b) internal pure returns (Accumulator memory) { return Accumulator({maker: a.maker.add(b.maker), taker: a.taker.add(b.taker)}); } /** * @notice Subtracts accumulator `b` from `a` * @param a The accumulator to subtract from * @param b The accumulator to subtract * @return The resulting subtracted accumulator */ function sub(Accumulator memory a, Accumulator memory b) internal pure returns (Accumulator memory) { return Accumulator({maker: a.maker.sub(b.maker), taker: a.taker.sub(b.taker)}); } /** * @notice Multiplies two accumulators together * @param a The first accumulator to multiply * @param b The second accumulator to multiply * @return The resulting multiplied accumulator */ function mul(Accumulator memory a, Accumulator memory b) internal pure returns (Accumulator memory) { return Accumulator({maker: a.maker.mul(b.maker), taker: a.taker.mul(b.taker)}); } /** * @notice Sums the maker and taker together from a single accumulator * @param self The struct to operate on * @return The sum of its maker and taker */ function sum(Accumulator memory self) internal pure returns (Fixed18) { return self.maker.add(self.taker); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/PackedFixed18.sol"; import "./Accumulator.sol"; /// @dev PackedAccumulator type struct PackedAccumulator { /// @dev maker accumulator per share PackedFixed18 maker; /// @dev taker accumulator per share PackedFixed18 taker; } using PackedAccumulatorLib for PackedAccumulator global; /** * @title PackedAccumulatorLib * @dev A packed version of the Accumulator which takes up a single storage slot using `PackedFixed18` values. * @notice Library for the packed Accumulator type. */ library PackedAccumulatorLib { /** * @notice Creates an accumulator from a packed accumulator * @param self packed accumulator * @return New accumulator */ function unpack(PackedAccumulator memory self) internal pure returns (Accumulator memory) { return Accumulator({maker: self.maker.unpack(), taker: self.taker.unpack()}); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/PackedUFixed18.sol"; import "./Position.sol"; /// @dev PackedPosition type struct PackedPosition { /// @dev Quantity of the maker position PackedUFixed18 maker; /// @dev Quantity of the taker position PackedUFixed18 taker; } using PackedPositionLib for PackedPosition global; /** * @title PackedPositionLib * @dev A packed version of the Position which takes up a single storage slot using `PackedFixed18` values. * @notice Library for the packed Position type. */ library PackedPositionLib { /** * @notice Creates an position from a packed position * @param self packed position * @return New position */ function unpack(PackedPosition memory self) internal pure returns (Position memory) { return Position({maker: self.maker.unpack(), taker: self.taker.unpack()}); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@openzeppelin/contracts/utils/Address.sol"; import "../../interfaces/IContractPayoffProvider.sol"; /// @dev PayoffDefinition tyoe struct PayoffDefinition { PayoffDefinitionLib.PayoffType payoffType; PayoffDefinitionLib.PayoffDirection payoffDirection; bytes30 data; } using PayoffDefinitionLib for PayoffDefinition global; type PayoffDefinitionStorage is bytes32; using PayoffDefinitionStorageLib for PayoffDefinitionStorage global; /** * @title PayoffDefinitionLib * @dev Library that surfaces logic for PayoffDefinition type functionality * @notice Library for the PayoffDefinition type. Performs validity and price transformation based on the payoff definition type. */ library PayoffDefinitionLib { using Address for address; error PayoffDefinitionUnsupportedTransform(PayoffType payoffType, PayoffDirection payoffDirection); error PayoffDefinitionNotContract(PayoffType payoffType, bytes30 data); /// @dev Payoff function type enum enum PayoffType { PASSTHROUGH, CONTRACT } enum PayoffDirection { LONG, SHORT } /** * @notice Checks validity of the payoff definition * @param self a payoff definition * @return Whether the payoff definition is valid for it's given type */ function valid(PayoffDefinition memory self) internal view returns (bool) { if (self.payoffType == PayoffType.CONTRACT) return address(_providerContract(self)).isContract(); // All other payoff types should have no data return uint(bytes32(self.data)) == 0; } /** * @notice Transforms a price based on the payoff definition * @param self a payoff definition * @param price raw oracle price * @return Price transformed by the payoff definition function */ function transform( PayoffDefinition memory self, Fixed18 price ) internal view returns (Fixed18) { PayoffType payoffType = self.payoffType; PayoffDirection payoffDirection = self.payoffDirection; Fixed18 transformedPrice; // First get the price depending on the type if (payoffType == PayoffType.PASSTHROUGH) transformedPrice = price; else if (payoffType == PayoffType.CONTRACT) transformedPrice = _payoffFromContract(self, price); else revert PayoffDefinitionUnsupportedTransform(payoffType, payoffDirection); // Then transform it depending on the direction flag if (self.payoffDirection == PayoffDirection.LONG) return transformedPrice; else if (self.payoffDirection == PayoffDirection.SHORT) return transformedPrice.mul(Fixed18Lib.NEG_ONE); else revert PayoffDefinitionUnsupportedTransform(payoffType, payoffDirection); } /** * @notice Parses the data field into an address * @dev Reverts if payoffType is not CONTRACT * @param self a payoff definition * @return IContractPayoffProvider address */ function _providerContract( PayoffDefinition memory self ) private pure returns (IContractPayoffProvider) { if (self.payoffType != PayoffType.CONTRACT) revert PayoffDefinitionNotContract(self.payoffType, self.data); // Shift to pull the last 20 bytes, then cast to an address return IContractPayoffProvider(address(bytes20(self.data << 80))); } /** * @notice Performs a price transformation by calling the underlying payoff contract * @param self a payoff definition * @param price raw oracle price * @return Price transformed by the payoff definition function on the contract */ function _payoffFromContract( PayoffDefinition memory self, Fixed18 price ) private view returns (Fixed18) { bytes memory ret = address(_providerContract(self)).functionStaticCall( abi.encodeCall(IContractPayoffProvider.payoff, price) ); return Fixed18.wrap(abi.decode(ret, (int256))); } } /** * @title PayoffDefinitionStorageLib * @notice Library that surfaces storage read and writes for the PayoffDefinition type */ library PayoffDefinitionStorageLib { function read(PayoffDefinitionStorage self) internal view returns (PayoffDefinition memory) { return _storagePointer(self); } function store(PayoffDefinitionStorage self, PayoffDefinition memory value) internal { PayoffDefinition storage storagePointer = _storagePointer(self); storagePointer.payoffType = value.payoffType; storagePointer.payoffDirection = value.payoffDirection; storagePointer.data = value.data; } function _storagePointer( PayoffDefinitionStorage self ) private pure returns (PayoffDefinition storage pointer) { assembly { pointer.slot := self } // solhint-disable-line no-inline-assembly } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/UFixed18.sol"; /// @dev PendingFeeUpdates type. Fees can be between 0 and 1 ** 10^18, so uint64 is sufficient struct PendingFeeUpdates { bool makerFeeUpdated; uint64 pendingMakerFee; bool takerFeeUpdated; uint64 pendingTakerFee; bool positionFeeUpdated; uint64 pendingPositionFee; } using PendingFeeUpdatesLib for PendingFeeUpdates global; type PendingFeeUpdatesStorage is bytes32; using PendingFeeUpdatesStorageLib for PendingFeeUpdatesStorage global; /** * @title PendingFeeUpdatesLib * @dev Library that surfaces convenience functions for the PendingFeeUpdates type * @notice Library for the PendingFeeUpdates type. Allows for setting and reading fee updates and clearing state */ library PendingFeeUpdatesLib { error PendingFeeUpdatesUnsupportedValue(UFixed18 value); /** * @notice Updates the pending maker fee to `newMakerFee` and sets the `makerFeeUpdated` flag * @dev Reverts if `newMakerFee` is invalid * @param self PendingFeeUpdates struct * @param newMakerFee new maker fee value */ function updateMakerFee(PendingFeeUpdates memory self, UFixed18 newMakerFee) internal pure { if (UFixed18.unwrap(newMakerFee) > type(uint64).max) revert PendingFeeUpdatesUnsupportedValue(newMakerFee); self.pendingMakerFee = uint64(UFixed18.unwrap(newMakerFee)); self.makerFeeUpdated = true; } /// @dev Returns the UFixed18-wrapped pending maker fee function makerFee(PendingFeeUpdates memory self) internal pure returns (UFixed18) { return UFixed18.wrap(uint256(self.pendingMakerFee)); } /** * @notice Updates the pending taker fee to `newTakerFee` and sets the `takerFeeUpdated` flag * @dev Reverts if `newTakerFee` is invalid * @param self PendingFeeUpdates struct * @param newTakerFee new taker fee value */ function updateTakerFee(PendingFeeUpdates memory self, UFixed18 newTakerFee) internal pure { if (UFixed18.unwrap(newTakerFee) > type(uint64).max) revert PendingFeeUpdatesUnsupportedValue(newTakerFee); self.pendingTakerFee = uint64(UFixed18.unwrap(newTakerFee)); self.takerFeeUpdated = true; } /// @dev Returns the UFixed18-wrapped pending taker fee function takerFee(PendingFeeUpdates memory self) internal pure returns (UFixed18) { return UFixed18.wrap(uint256(self.pendingTakerFee)); } /** * @notice Updates the pending position fee to `newPositionFee` and sets the `positionFeeUpdated` flag * @dev Reverts if `newPositionFee` is invalid * @param self PendingFeeUpdates struct * @param newPositionFee new position fee value */ function updatePositionFee(PendingFeeUpdates memory self, UFixed18 newPositionFee) internal pure { if (UFixed18.unwrap(newPositionFee) > type(uint64).max) revert PendingFeeUpdatesUnsupportedValue(newPositionFee); self.pendingPositionFee = uint64(UFixed18.unwrap(newPositionFee)); self.positionFeeUpdated = true; } /// @dev Returns the UFixed18-wrapped pending position fee function positionFee(PendingFeeUpdates memory self) internal pure returns (UFixed18) { return UFixed18.wrap(uint256(self.pendingPositionFee)); } /// @dev Returns true if any of the updated flags are true function hasUpdates(PendingFeeUpdates memory self) internal pure returns (bool) { return self.makerFeeUpdated || self.takerFeeUpdated || self.positionFeeUpdated; } /// @dev Resets all struct values to defaults function clear(PendingFeeUpdates memory self) internal pure { self.makerFeeUpdated = false; self.pendingMakerFee = 0; self.takerFeeUpdated = false; self.pendingTakerFee = 0; self.positionFeeUpdated = false; self.pendingPositionFee = 0; } } /** * @title PendingFeeUpdatesStorageLib * @notice Library that surfaces storage read and writes for the PendingFeeUpdates type */ library PendingFeeUpdatesStorageLib { struct PendingFeeUpdatesStoragePointer { PendingFeeUpdates value; } function read(PendingFeeUpdatesStorage self) internal view returns (PendingFeeUpdates memory) { return _storagePointer(self).value; } function store(PendingFeeUpdatesStorage self, PendingFeeUpdates memory value) internal { _storagePointer(self).value = value; } function _storagePointer( PendingFeeUpdatesStorage self ) private pure returns (PendingFeeUpdatesStoragePointer storage pointer) { /// @solidity memory-safe-assembly assembly { pointer.slot := self } // solhint-disable-line no-inline-assembly } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@openzeppelin/contracts/utils/math/Math.sol"; import "@equilibria/root/number/types/UFixed18.sol"; import "../IProduct.sol"; import "./Accumulator.sol"; import "./PrePosition.sol"; import "./PackedPosition.sol"; /// @dev Position type struct Position { /// @dev Quantity of the maker position UFixed18 maker; /// @dev Quantity of the taker position UFixed18 taker; } using PositionLib for Position global; /** * @title PositionLib * @notice Library that surfaces math and settlement computations for the Position type. * @dev Positions track the current quantity of the account's maker and taker positions respectively * denominated as a unit of the product's payoff function. */ library PositionLib { /** * @notice Creates a packed position from an position * @param self A position * @return New packed position */ function pack(Position memory self) internal pure returns (PackedPosition memory) { return PackedPosition({maker: self.maker.pack(), taker: self.taker.pack()}); } /** * @notice Returns whether the position is fully empty * @param self A position * @return Whether the position is empty */ function isEmpty(Position memory self) internal pure returns (bool) { return self.maker.isZero() && self.taker.isZero(); } /** * @notice Adds position `a` and `b` together, returning the result * @param a The first position to sum * @param b The second position to sum * @return Resulting summed position */ function add(Position memory a, Position memory b) internal pure returns (Position memory) { return Position({maker: a.maker.add(b.maker), taker: a.taker.add(b.taker)}); } /** * @notice Subtracts position `b` from `a`, returning the result * @param a The position to subtract from * @param b The position to subtract * @return Resulting subtracted position */ function sub(Position memory a, Position memory b) internal pure returns (Position memory) { return Position({maker: a.maker.sub(b.maker), taker: a.taker.sub(b.taker)}); } /** * @notice Multiplies position `self` by accumulator `accumulator` and returns the resulting accumulator * @param self The Position to operate on * @param accumulator The accumulator to multiply by * @return Resulting multiplied accumulator */ function mul(Position memory self, Accumulator memory accumulator) internal pure returns (Accumulator memory) { return Accumulator({ maker: Fixed18Lib.from(self.maker).mul(accumulator.maker), taker: Fixed18Lib.from(self.taker).mul(accumulator.taker) }); } /** * @notice Scales position `self` by fixed-decimal `scale` and returns the resulting position * @param self The Position to operate on * @param scale The Fixed-decimal to scale by * @return Resulting scaled position */ function mul(Position memory self, UFixed18 scale) internal pure returns (Position memory) { return Position({maker: self.maker.mul(scale), taker: self.taker.mul(scale)}); } /** * @notice Divides position `self` by `b` and returns the resulting accumulator * @param self The Position to operate on * @param b The number to divide by * @return Resulting divided accumulator */ function div(Position memory self, uint256 b) internal pure returns (Accumulator memory) { return Accumulator({ maker: Fixed18Lib.from(self.maker).div(Fixed18Lib.from(UFixed18Lib.from(b))), taker: Fixed18Lib.from(self.taker).div(Fixed18Lib.from(UFixed18Lib.from(b))) }); } /** * @notice Returns the maximum of `self`'s maker and taker values * @param self The struct to operate on * @return Resulting maximum value */ function max(Position memory self) internal pure returns (UFixed18) { return UFixed18Lib.max(self.maker, self.taker); } /** * @notice Sums the maker and taker together from a single position * @param self The struct to operate on * @return The sum of its maker and taker */ function sum(Position memory self) internal pure returns (UFixed18) { return self.maker.add(self.taker); } /** * @notice Computes the next position after the pending-settlement position delta is included * @param self The current Position * @param pre The pending-settlement position delta * @return Next Position */ function next(Position memory self, PrePosition memory pre) internal pure returns (Position memory) { return sub(add(self, pre.openPosition), pre.closePosition); } /** * @notice Returns the settled position at oracle version `toOracleVersion` * @dev Checks if a new position is ready to be settled based on the provided `toOracleVersion` * and `pre` and returns accordingly * @param self The current Position * @param pre The pending-settlement position delta * @param toOracleVersion The oracle version to settle to * @return Settled position at oracle version * @return Whether a new position was settled */ function settled( Position memory self, PrePosition memory pre, IOracleProvider.OracleVersion memory toOracleVersion ) internal pure returns (Position memory, bool) { return pre.canSettle(toOracleVersion) ? (next(self, pre), true) : (self, false); } /** * @notice Returns the socialization factor for the current position * @dev Socialization account for the case where `taker` > `maker` temporarily due to a liquidation * on the maker side. This dampens the taker's exposure pro-rata to ensure that the maker side * is never exposed over 1 x short. * @param self The Position to operate on * @return Socialization factor */ function socializationFactor(Position memory self) internal pure returns (UFixed18) { return self.taker.isZero() ? UFixed18Lib.ONE : UFixed18Lib.min(UFixed18Lib.ONE, self.maker.div(self.taker)); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/number/types/UFixed18.sol"; import "@equilibria/perennial-oracle/contracts/interfaces/IOracleProvider.sol"; import "./Position.sol"; import "../IProduct.sol"; /// @dev PrePosition type struct PrePosition { /// @dev Oracle version at which the new position delta was recorded uint256 oracleVersion; /// @dev Size of position to open at oracle version Position openPosition; /// @dev Size of position to close at oracle version Position closePosition; } using PrePositionLib for PrePosition global; /** * @title PrePositionLib * @notice Library that manages a pre-settlement position delta. * @dev PrePositions track the currently awaiting-settlement deltas to a settled Position. These are * Primarily necessary to introduce lag into the settlement system such that oracle lag cannot be * gamed to a user's advantage. When a user opens or closes a new position, it sits as a PrePosition * for one oracle version until it's settle into the Position, making it then effective. PrePositions * are automatically settled at the correct oracle version even if a flywheel call doesn't happen until * several version into the future by using the historical version lookups in the corresponding "Versioned" * global state types. */ library PrePositionLib { /** * @notice Returns whether there is no pending-settlement position delta * @param self The struct to operate on * @return Whether the pending-settlement position delta is empty */ function isEmpty(PrePosition memory self) internal pure returns (bool) { return self.openPosition.isEmpty() && self.closePosition.isEmpty(); } /** * @notice Increments the maker side of the open position delta * @param self The struct to operate on * @param currentVersion The current oracle version index * @param amount The position amount to open */ function openMake(PrePosition storage self, uint256 currentVersion, UFixed18 amount) internal { self.openPosition.maker = self.openPosition.maker.add(amount); self.oracleVersion = currentVersion; } /** * @notice Increments the maker side of the close position delta * @param self The struct to operate on * @param currentVersion The current oracle version index * @param amount The maker position amount to close */ function closeMake(PrePosition storage self, uint256 currentVersion, UFixed18 amount) internal { self.closePosition.maker = self.closePosition.maker.add(amount); self.oracleVersion = currentVersion; } /** * @notice Increments the taker side of the open position delta * @param self The struct to operate on * @param currentVersion The current oracle version index * @param amount The taker position amount to open */ function openTake(PrePosition storage self, uint256 currentVersion, UFixed18 amount) internal { self.openPosition.taker = self.openPosition.taker.add(amount); self.oracleVersion = currentVersion; } /** * @notice Increments the taker side of the close position delta * @param self The struct to operate on * @param currentVersion The current oracle version index * @param amount The taker position amount to close */ function closeTake(PrePosition storage self, uint256 currentVersion, UFixed18 amount) internal { self.closePosition.taker = self.closePosition.taker.add(amount); self.oracleVersion = currentVersion; } /** * @notice Returns whether the the pending position delta can be settled at version `toOracleVersion` * @dev Pending-settlement positions deltas can be settled (1) oracle version after they are recorded * @param self The struct to operate on * @param toOracleVersion The potential oracle version to settle * @return Whether the position delta can be settled */ function canSettle( PrePosition memory self, IOracleProvider.OracleVersion memory toOracleVersion ) internal pure returns (bool) { return !isEmpty(self) && toOracleVersion.version > self.oracleVersion; } /** * @notice Computes the fee incurred for opening or closing the pending-settlement position * @dev Must be called from a valid product to get the proper fee amounts * @param self The struct to operate on * @param latestOracleVersion The oracle version at which position was modified * @return The maker / taker fee incurred */ function computeFee( PrePosition memory self, IOracleProvider.OracleVersion memory latestOracleVersion ) internal view returns (Position memory) { Position memory positionDelta = self.openPosition.add(self.closePosition); (UFixed18 makerNotional, UFixed18 takerNotional) = ( Fixed18Lib.from(positionDelta.maker).mul(latestOracleVersion.price).abs(), Fixed18Lib.from(positionDelta.taker).mul(latestOracleVersion.price).abs() ); IProduct product = IProduct(address(this)); return Position(makerNotional.mul(product.makerFee()), takerNotional.mul(product.takerFee())); } /** * @notice Computes the next oracle version to settle * @dev - If there is no pending-settlement position delta, returns the current oracle version * - Otherwise returns the oracle version at which the pending-settlement position delta can be first settled * * Corresponds to point (b) in the Position settlement flow * @param self The struct to operate on * @param currentVersion The current oracle version index * @return Next oracle version to settle */ function settleVersion(PrePosition storage self, uint256 currentVersion) internal view returns (uint256) { uint256 _oracleVersion = self.oracleVersion; return _oracleVersion == 0 ? currentVersion : _oracleVersion + 1; } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@equilibria/root/token/types/Token18.sol"; import "../IProduct.sol"; import "./Position.sol"; import "./Accumulator.sol"; /// @dev ProgramInfo type struct ProgramInfo { /// @dev Coordinator for this program uint256 coordinatorId; /// @dev Amount of total maker and taker rewards Position amount; /// @dev start timestamp of the program uint256 start; /// @dev duration of the program (in seconds) uint256 duration; /** * @dev Reward ERC20 token contract * @notice Perennial does not support non-standard ERC20s as reward tokens for incentive programs, including, but not limited to: fee on transfer and rebase tokens. Using such a non-standard token will likely result in loss of funds. */ Token18 token; } using ProgramInfoLib for ProgramInfo global; /** * @title ProgramInfoLib * @notice Library that snapshots the static information for a single program. * @dev This information does not change during the operation of a program. */ library ProgramInfoLib { uint256 private constant MIN_DURATION = 1 days; uint256 private constant MAX_DURATION = 2 * 365 days; error ProgramInvalidStartError(); error ProgramInvalidDurationError(); /** * @notice Validates and creates a new Program * @dev Reverts for invalid programInfos * @param programInfo Un-sanitized static program information */ function validate(ProgramInfo memory programInfo) internal view { if (isStarted(programInfo, block.timestamp)) revert ProgramInvalidStartError(); if (programInfo.duration < MIN_DURATION || programInfo.duration > MAX_DURATION) revert ProgramInvalidDurationError(); } /** * @notice Computes a new program info with the fee taken out of the amount * @param programInfo Original program info * @param incentivizationFee The incentivization fee * @return New program info * @return Fee amount */ function deductFee(ProgramInfo memory programInfo, UFixed18 incentivizationFee) internal pure returns (ProgramInfo memory, UFixed18) { Position memory newProgramAmount = programInfo.amount.mul(UFixed18Lib.ONE.sub(incentivizationFee)); UFixed18 programFeeAmount = programInfo.amount.sub(newProgramAmount).sum(); programInfo.amount = newProgramAmount; return (programInfo, programFeeAmount); } /** * @notice Returns the maker and taker amounts per position share * @param self The ProgramInfo to operate on * @return programFee Amounts per share */ function amountPerShare(ProgramInfo memory self) internal pure returns (Accumulator memory) { return self.amount.div(self.duration); } /** * @notice Returns whether the program has started by timestamp `timestamp` * @param self The ProgramInfo to operate on * @param timestamp Timestamp to check for * @return Whether the program has started */ function isStarted(ProgramInfo memory self, uint256 timestamp) internal pure returns (bool) { return timestamp >= self.start; } /** * @notice Returns whether the program is completed by timestamp `timestamp` * @param self The ProgramInfo to operate on * @param timestamp Timestamp to check for * @return Whether the program is completed */ function isComplete(ProgramInfo memory self, uint256 timestamp) internal pure returns (bool) { return timestamp >= (self.start + self.duration); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@openzeppelin/contracts/utils/Address.sol"; import "../../storage/UStorage.sol"; /** * @title UInitializable * @notice Library to manage the initialization lifecycle of upgradeable contracts * @dev `UInitializable` allows the creation of pseudo-constructors for upgradeable contracts. One * `initializer` should be declared per top-level contract. Child contracts can use the `onlyInitializer` * modifier to tag their internal initialization functions to ensure that they can only be called * from a top-level `initializer` or a constructor. */ abstract contract UInitializable { error UInitializableZeroVersionError(); error UInitializableAlreadyInitializedError(uint256 version); error UInitializableNotInitializingError(); event Initialized(uint256 version); /// @dev The initialized flag Uint256Storage private constant _version = Uint256Storage.wrap(keccak256("equilibria.root.UInitializable.version")); /// @dev The initializing flag BoolStorage private constant _initializing = BoolStorage.wrap(keccak256("equilibria.root.UInitializable.initializing")); /// @dev Can only be called once per version, `version` is 1-indexed modifier initializer(uint256 version) { if (version == 0) revert UInitializableZeroVersionError(); if (_version.read() >= version) revert UInitializableAlreadyInitializedError(version); _version.store(version); _initializing.store(true); _; _initializing.store(false); emit Initialized(version); } /// @dev Can only be called from an initializer or constructor modifier onlyInitializer() { if (!_constructing() && !_initializing.read()) revert UInitializableNotInitializingError(); _; } /** * @notice Returns whether the contract is currently being constructed * @dev {Address.isContract} returns false for contracts currently in the process of being constructed * @return Whether the contract is currently being constructed */ function _constructing() private view returns (bool) { return !Address.isContract(address(this)); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "../number/types/UFixed18.sol"; import "../number/types/Fixed18.sol"; /** * @title CurveMath * @notice Library for managing math operations for utilization curves. */ library CurveMath { error CurveMathOutOfBoundsError(); /** * @notice Computes a linear interpolation between two points * @param startX First point's x-coordinate * @param startY First point's y-coordinate * @param endX Second point's x-coordinate * @param endY Second point's y-coordinate * @param targetX x-coordinate to interpolate * @return y-coordinate for `targetX` along the line from (`startX`, `startY`) -> (`endX`, `endY`) */ function linearInterpolation( UFixed18 startX, Fixed18 startY, UFixed18 endX, Fixed18 endY, UFixed18 targetX ) internal pure returns (Fixed18) { if (targetX.lt(startX) || targetX.gt(endX)) revert CurveMathOutOfBoundsError(); UFixed18 xRange = endX.sub(startX); Fixed18 yRange = endY.sub(startY); UFixed18 xRatio = targetX.sub(startX).div(xRange); return yRange.mul(Fixed18Lib.from(xRatio)).add(startY); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "../CurveMath.sol"; import "../../number/types/PackedUFixed18.sol"; import "../../number/types/PackedFixed18.sol"; /// @dev JumpRateUtilizationCurve type struct JumpRateUtilizationCurve { PackedFixed18 minRate; PackedFixed18 maxRate; PackedFixed18 targetRate; PackedUFixed18 targetUtilization; } using JumpRateUtilizationCurveLib for JumpRateUtilizationCurve global; type JumpRateUtilizationCurveStorage is bytes32; using JumpRateUtilizationCurveStorageLib for JumpRateUtilizationCurveStorage global; /** * @title JumpRateUtilizationCurveLib * @notice Library for the Jump Rate utilization curve type */ library JumpRateUtilizationCurveLib { /** * @notice Computes the corresponding rate for a utilization ratio * @param utilization The utilization ratio * @return The corresponding rate */ function compute(JumpRateUtilizationCurve memory self, UFixed18 utilization) internal pure returns (Fixed18) { UFixed18 targetUtilization = self.targetUtilization.unpack(); if (utilization.lt(targetUtilization)) { return CurveMath.linearInterpolation( UFixed18Lib.ZERO, self.minRate.unpack(), targetUtilization, self.targetRate.unpack(), utilization ); } if (utilization.lt(UFixed18Lib.ONE)) { return CurveMath.linearInterpolation( targetUtilization, self.targetRate.unpack(), UFixed18Lib.ONE, self.maxRate.unpack(), utilization ); } return self.maxRate.unpack(); } } library JumpRateUtilizationCurveStorageLib { function read(JumpRateUtilizationCurveStorage self) internal view returns (JumpRateUtilizationCurve memory) { return _storagePointer(self); } function store(JumpRateUtilizationCurveStorage self, JumpRateUtilizationCurve memory value) internal { JumpRateUtilizationCurve storage storagePointer = _storagePointer(self); storagePointer.minRate = value.minRate; storagePointer.maxRate = value.maxRate; storagePointer.targetRate = value.targetRate; storagePointer.targetUtilization = value.targetUtilization; } function _storagePointer(JumpRateUtilizationCurveStorage self) private pure returns (JumpRateUtilizationCurve storage pointer) { assembly { pointer.slot := self } } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@openzeppelin/contracts/utils/math/SignedMath.sol"; import "./UFixed18.sol"; import "./PackedFixed18.sol"; /// @dev Fixed18 type type Fixed18 is int256; using Fixed18Lib for Fixed18 global; type Fixed18Storage is bytes32; using Fixed18StorageLib for Fixed18Storage global; /** * @title Fixed18Lib * @notice Library for the signed fixed-decimal type. */ library Fixed18Lib { error Fixed18OverflowError(uint256 value); error Fixed18PackingOverflowError(int256 value); error Fixed18PackingUnderflowError(int256 value); int256 private constant BASE = 1e18; Fixed18 public constant ZERO = Fixed18.wrap(0); Fixed18 public constant ONE = Fixed18.wrap(BASE); Fixed18 public constant NEG_ONE = Fixed18.wrap(-1 * BASE); Fixed18 public constant MAX = Fixed18.wrap(type(int256).max); Fixed18 public constant MIN = Fixed18.wrap(type(int256).min); /** * @notice Creates a signed fixed-decimal from an unsigned fixed-decimal * @param a Unsigned fixed-decimal * @return New signed fixed-decimal */ function from(UFixed18 a) internal pure returns (Fixed18) { uint256 value = UFixed18.unwrap(a); if (value > uint256(type(int256).max)) revert Fixed18OverflowError(value); return Fixed18.wrap(int256(value)); } /** * @notice Creates a signed fixed-decimal from a sign and an unsigned fixed-decimal * @param s Sign * @param m Unsigned fixed-decimal magnitude * @return New signed fixed-decimal */ function from(int256 s, UFixed18 m) internal pure returns (Fixed18) { if (s > 0) return from(m); if (s < 0) return Fixed18.wrap(-1 * Fixed18.unwrap(from(m))); return ZERO; } /** * @notice Creates a signed fixed-decimal from a signed integer * @param a Signed number * @return New signed fixed-decimal */ function from(int256 a) internal pure returns (Fixed18) { return Fixed18.wrap(a * BASE); } /** * @notice Creates a packed signed fixed-decimal from an signed fixed-decimal * @param a signed fixed-decimal * @return New packed signed fixed-decimal */ function pack(Fixed18 a) internal pure returns (PackedFixed18) { int256 value = Fixed18.unwrap(a); if (value > type(int128).max) revert Fixed18PackingOverflowError(value); if (value < type(int128).min) revert Fixed18PackingUnderflowError(value); return PackedFixed18.wrap(int128(value)); } /** * @notice Returns whether the signed fixed-decimal is equal to zero. * @param a Signed fixed-decimal * @return Whether the signed fixed-decimal is zero. */ function isZero(Fixed18 a) internal pure returns (bool) { return Fixed18.unwrap(a) == 0; } /** * @notice Adds two signed fixed-decimals `a` and `b` together * @param a First signed fixed-decimal * @param b Second signed fixed-decimal * @return Resulting summed signed fixed-decimal */ function add(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) { return Fixed18.wrap(Fixed18.unwrap(a) + Fixed18.unwrap(b)); } /** * @notice Subtracts signed fixed-decimal `b` from `a` * @param a Signed fixed-decimal to subtract from * @param b Signed fixed-decimal to subtract * @return Resulting subtracted signed fixed-decimal */ function sub(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) { return Fixed18.wrap(Fixed18.unwrap(a) - Fixed18.unwrap(b)); } /** * @notice Multiplies two signed fixed-decimals `a` and `b` together * @param a First signed fixed-decimal * @param b Second signed fixed-decimal * @return Resulting multiplied signed fixed-decimal */ function mul(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) { return Fixed18.wrap(Fixed18.unwrap(a) * Fixed18.unwrap(b) / BASE); } /** * @notice Divides signed fixed-decimal `a` by `b` * @param a Signed fixed-decimal to divide * @param b Signed fixed-decimal to divide by * @return Resulting divided signed fixed-decimal */ function div(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) { return Fixed18.wrap(Fixed18.unwrap(a) * BASE / Fixed18.unwrap(b)); } /** * @notice Divides unsigned fixed-decimal `a` by `b` * @dev Does not revert on divide-by-0, instead returns `ONE` for `0/0`, `MAX` for `n/0`, and `MIN` for `-n/0`. * @param a Unsigned fixed-decimal to divide * @param b Unsigned fixed-decimal to divide by * @return Resulting divided unsigned fixed-decimal */ function unsafeDiv(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) { if (isZero(b)) { if (gt(a, ZERO)) return MAX; if (lt(a, ZERO)) return MIN; return ONE; } else { return div(a, b); } } /** * @notice Computes a * b / c without loss of precision due to BASE conversion * @param a First signed fixed-decimal * @param b Signed number to multiply by * @param c Signed number to divide by * @return Resulting computation */ function muldiv(Fixed18 a, int256 b, int256 c) internal pure returns (Fixed18) { return muldiv(a, Fixed18.wrap(b), Fixed18.wrap(c)); } /** * @notice Computes a * b / c without loss of precision due to BASE conversion * @param a First signed fixed-decimal * @param b Signed fixed-decimal to multiply by * @param c Signed fixed-decimal to divide by * @return Resulting computation */ function muldiv(Fixed18 a, Fixed18 b, Fixed18 c) internal pure returns (Fixed18) { return Fixed18.wrap(Fixed18.unwrap(a) * Fixed18.unwrap(b) / Fixed18.unwrap(c)); } /** * @notice Returns whether signed fixed-decimal `a` is equal to `b` * @param a First signed fixed-decimal * @param b Second signed fixed-decimal * @return Whether `a` is equal to `b` */ function eq(Fixed18 a, Fixed18 b) internal pure returns (bool) { return compare(a, b) == 1; } /** * @notice Returns whether signed fixed-decimal `a` is greater than `b` * @param a First signed fixed-decimal * @param b Second signed fixed-decimal * @return Whether `a` is greater than `b` */ function gt(Fixed18 a, Fixed18 b) internal pure returns (bool) { return compare(a, b) == 2; } /** * @notice Returns whether signed fixed-decimal `a` is less than `b` * @param a First signed fixed-decimal * @param b Second signed fixed-decimal * @return Whether `a` is less than `b` */ function lt(Fixed18 a, Fixed18 b) internal pure returns (bool) { return compare(a, b) == 0; } /** * @notice Returns whether signed fixed-decimal `a` is greater than or equal to `b` * @param a First signed fixed-decimal * @param b Second signed fixed-decimal * @return Whether `a` is greater than or equal to `b` */ function gte(Fixed18 a, Fixed18 b) internal pure returns (bool) { return gt(a, b) || eq(a, b); } /** * @notice Returns whether signed fixed-decimal `a` is less than or equal to `b` * @param a First signed fixed-decimal * @param b Second signed fixed-decimal * @return Whether `a` is less than or equal to `b` */ function lte(Fixed18 a, Fixed18 b) internal pure returns (bool) { return lt(a, b) || eq(a, b); } /** * @notice Compares the signed fixed-decimals `a` and `b` * @dev Returns: 2 for greater than * 1 for equal to * 0 for less than * @param a First signed fixed-decimal * @param b Second signed fixed-decimal * @return Compare result of `a` and `b` */ function compare(Fixed18 a, Fixed18 b) internal pure returns (uint256) { (int256 au, int256 bu) = (Fixed18.unwrap(a), Fixed18.unwrap(b)); if (au > bu) return 2; if (au < bu) return 0; return 1; } /** * @notice Returns a signed fixed-decimal representing the ratio of `a` over `b` * @param a First signed number * @param b Second signed number * @return Ratio of `a` over `b` */ function ratio(int256 a, int256 b) internal pure returns (Fixed18) { return Fixed18.wrap(a * BASE / b); } /** * @notice Returns the minimum of signed fixed-decimals `a` and `b` * @param a First signed fixed-decimal * @param b Second signed fixed-decimal * @return Minimum of `a` and `b` */ function min(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) { return Fixed18.wrap(SignedMath.min(Fixed18.unwrap(a), Fixed18.unwrap(b))); } /** * @notice Returns the maximum of signed fixed-decimals `a` and `b` * @param a First signed fixed-decimal * @param b Second signed fixed-decimal * @return Maximum of `a` and `b` */ function max(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) { return Fixed18.wrap(SignedMath.max(Fixed18.unwrap(a), Fixed18.unwrap(b))); } /** * @notice Converts the signed fixed-decimal into an integer, truncating any decimal portion * @param a Signed fixed-decimal * @return Truncated signed number */ function truncate(Fixed18 a) internal pure returns (int256) { return Fixed18.unwrap(a) / BASE; } /** * @notice Returns the sign of the signed fixed-decimal * @dev Returns: -1 for negative * 0 for zero * 1 for positive * @param a Signed fixed-decimal * @return Sign of the signed fixed-decimal */ function sign(Fixed18 a) internal pure returns (int256) { if (Fixed18.unwrap(a) > 0) return 1; if (Fixed18.unwrap(a) < 0) return -1; return 0; } /** * @notice Returns the absolute value of the signed fixed-decimal * @param a Signed fixed-decimal * @return Absolute value of the signed fixed-decimal */ function abs(Fixed18 a) internal pure returns (UFixed18) { return UFixed18.wrap(SignedMath.abs(Fixed18.unwrap(a))); } } library Fixed18StorageLib { function read(Fixed18Storage self) internal view returns (Fixed18 value) { assembly { value := sload(self) } } function store(Fixed18Storage self, Fixed18 value) internal { assembly { sstore(self, value) } } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "./Fixed18.sol"; /// @dev PackedFixed18 type type PackedFixed18 is int128; using PackedFixed18Lib for PackedFixed18 global; /** * @title PackedFixed18Lib * @dev A packed version of the Fixed18 which takes up half the storage space (two PackedFixed18 can be packed * into a single slot). Only valid within the range -1.7014118e+20 <= x <= 1.7014118e+20. * @notice Library for the packed signed fixed-decimal type. */ library PackedFixed18Lib { PackedFixed18 public constant MAX = PackedFixed18.wrap(type(int128).max); PackedFixed18 public constant MIN = PackedFixed18.wrap(type(int128).min); /** * @notice Creates an unpacked signed fixed-decimal from a packed signed fixed-decimal * @param self packed signed fixed-decimal * @return New unpacked signed fixed-decimal */ function unpack(PackedFixed18 self) internal pure returns (Fixed18) { return Fixed18.wrap(int256(PackedFixed18.unwrap(self))); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "./UFixed18.sol"; /// @dev PackedUFixed18 type type PackedUFixed18 is uint128; using PackedUFixed18Lib for PackedUFixed18 global; /** * @title PackedUFixed18Lib * @dev A packed version of the UFixed18 which takes up half the storage space (two PackedUFixed18 can be packed * into a single slot). Only valid within the range 0 <= x <= 3.4028237e+20. * @notice Library for the packed unsigned fixed-decimal type. */ library PackedUFixed18Lib { PackedUFixed18 public constant MAX = PackedUFixed18.wrap(type(uint128).max); /** * @notice Creates an unpacked unsigned fixed-decimal from a packed unsigned fixed-decimal * @param self packed unsigned fixed-decimal * @return New unpacked unsigned fixed-decimal */ function unpack(PackedUFixed18 self) internal pure returns (UFixed18) { return UFixed18.wrap(uint256(PackedUFixed18.unwrap(self))); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@openzeppelin/contracts/utils/math/Math.sol"; import "./Fixed18.sol"; import "./PackedUFixed18.sol"; /// @dev UFixed18 type type UFixed18 is uint256; using UFixed18Lib for UFixed18 global; type UFixed18Storage is bytes32; using UFixed18StorageLib for UFixed18Storage global; /** * @title UFixed18Lib * @notice Library for the unsigned fixed-decimal type. */ library UFixed18Lib { error UFixed18UnderflowError(int256 value); error UFixed18PackingOverflowError(uint256 value); uint256 private constant BASE = 1e18; UFixed18 public constant ZERO = UFixed18.wrap(0); UFixed18 public constant ONE = UFixed18.wrap(BASE); UFixed18 public constant MAX = UFixed18.wrap(type(uint256).max); /** * @notice Creates a unsigned fixed-decimal from a signed fixed-decimal * @param a Signed fixed-decimal * @return New unsigned fixed-decimal */ function from(Fixed18 a) internal pure returns (UFixed18) { int256 value = Fixed18.unwrap(a); if (value < 0) revert UFixed18UnderflowError(value); return UFixed18.wrap(uint256(value)); } /** * @notice Creates a unsigned fixed-decimal from a unsigned integer * @param a Unsigned number * @return New unsigned fixed-decimal */ function from(uint256 a) internal pure returns (UFixed18) { return UFixed18.wrap(a * BASE); } /** * @notice Creates a packed unsigned fixed-decimal from an unsigned fixed-decimal * @param a unsigned fixed-decimal * @return New packed unsigned fixed-decimal */ function pack(UFixed18 a) internal pure returns (PackedUFixed18) { uint256 value = UFixed18.unwrap(a); if (value > type(uint128).max) revert UFixed18PackingOverflowError(value); return PackedUFixed18.wrap(uint128(value)); } /** * @notice Returns whether the unsigned fixed-decimal is equal to zero. * @param a Unsigned fixed-decimal * @return Whether the unsigned fixed-decimal is zero. */ function isZero(UFixed18 a) internal pure returns (bool) { return UFixed18.unwrap(a) == 0; } /** * @notice Adds two unsigned fixed-decimals `a` and `b` together * @param a First unsigned fixed-decimal * @param b Second unsigned fixed-decimal * @return Resulting summed unsigned fixed-decimal */ function add(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) { return UFixed18.wrap(UFixed18.unwrap(a) + UFixed18.unwrap(b)); } /** * @notice Subtracts unsigned fixed-decimal `b` from `a` * @param a Unsigned fixed-decimal to subtract from * @param b Unsigned fixed-decimal to subtract * @return Resulting subtracted unsigned fixed-decimal */ function sub(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) { return UFixed18.wrap(UFixed18.unwrap(a) - UFixed18.unwrap(b)); } /** * @notice Multiplies two unsigned fixed-decimals `a` and `b` together * @param a First unsigned fixed-decimal * @param b Second unsigned fixed-decimal * @return Resulting multiplied unsigned fixed-decimal */ function mul(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) { return UFixed18.wrap(UFixed18.unwrap(a) * UFixed18.unwrap(b) / BASE); } /** * @notice Divides unsigned fixed-decimal `a` by `b` * @param a Unsigned fixed-decimal to divide * @param b Unsigned fixed-decimal to divide by * @return Resulting divided unsigned fixed-decimal */ function div(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) { return UFixed18.wrap(UFixed18.unwrap(a) * BASE / UFixed18.unwrap(b)); } /** * @notice Divides unsigned fixed-decimal `a` by `b` * @dev Does not revert on divide-by-0, instead returns `ONE` for `0/0` and `MAX` for `n/0`. * @param a Unsigned fixed-decimal to divide * @param b Unsigned fixed-decimal to divide by * @return Resulting divided unsigned fixed-decimal */ function unsafeDiv(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) { if (isZero(b)) { return isZero(a) ? ONE : MAX; } else { return div(a, b); } } /** * @notice Computes a * b / c without loss of precision due to BASE conversion * @param a First unsigned fixed-decimal * @param b Unsigned number to multiply by * @param c Unsigned number to divide by * @return Resulting computation */ function muldiv(UFixed18 a, uint256 b, uint256 c) internal pure returns (UFixed18) { return muldiv(a, UFixed18.wrap(b), UFixed18.wrap(c)); } /** * @notice Computes a * b / c without loss of precision due to BASE conversion * @param a First unsigned fixed-decimal * @param b Unsigned fixed-decimal to multiply by * @param c Unsigned fixed-decimal to divide by * @return Resulting computation */ function muldiv(UFixed18 a, UFixed18 b, UFixed18 c) internal pure returns (UFixed18) { return UFixed18.wrap(UFixed18.unwrap(a) * UFixed18.unwrap(b) / UFixed18.unwrap(c)); } /** * @notice Returns whether unsigned fixed-decimal `a` is equal to `b` * @param a First unsigned fixed-decimal * @param b Second unsigned fixed-decimal * @return Whether `a` is equal to `b` */ function eq(UFixed18 a, UFixed18 b) internal pure returns (bool) { return compare(a, b) == 1; } /** * @notice Returns whether unsigned fixed-decimal `a` is greater than `b` * @param a First unsigned fixed-decimal * @param b Second unsigned fixed-decimal * @return Whether `a` is greater than `b` */ function gt(UFixed18 a, UFixed18 b) internal pure returns (bool) { return compare(a, b) == 2; } /** * @notice Returns whether unsigned fixed-decimal `a` is less than `b` * @param a First unsigned fixed-decimal * @param b Second unsigned fixed-decimal * @return Whether `a` is less than `b` */ function lt(UFixed18 a, UFixed18 b) internal pure returns (bool) { return compare(a, b) == 0; } /** * @notice Returns whether unsigned fixed-decimal `a` is greater than or equal to `b` * @param a First unsigned fixed-decimal * @param b Second unsigned fixed-decimal * @return Whether `a` is greater than or equal to `b` */ function gte(UFixed18 a, UFixed18 b) internal pure returns (bool) { return gt(a, b) || eq(a, b); } /** * @notice Returns whether unsigned fixed-decimal `a` is less than or equal to `b` * @param a First unsigned fixed-decimal * @param b Second unsigned fixed-decimal * @return Whether `a` is less than or equal to `b` */ function lte(UFixed18 a, UFixed18 b) internal pure returns (bool) { return lt(a, b) || eq(a, b); } /** * @notice Compares the unsigned fixed-decimals `a` and `b` * @dev Returns: 2 for greater than * 1 for equal to * 0 for less than * @param a First unsigned fixed-decimal * @param b Second unsigned fixed-decimal * @return Compare result of `a` and `b` */ function compare(UFixed18 a, UFixed18 b) internal pure returns (uint256) { (uint256 au, uint256 bu) = (UFixed18.unwrap(a), UFixed18.unwrap(b)); if (au > bu) return 2; if (au < bu) return 0; return 1; } /** * @notice Returns a unsigned fixed-decimal representing the ratio of `a` over `b` * @param a First unsigned number * @param b Second unsigned number * @return Ratio of `a` over `b` */ function ratio(uint256 a, uint256 b) internal pure returns (UFixed18) { return UFixed18.wrap(a * BASE / b); } /** * @notice Returns the minimum of unsigned fixed-decimals `a` and `b` * @param a First unsigned fixed-decimal * @param b Second unsigned fixed-decimal * @return Minimum of `a` and `b` */ function min(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) { return UFixed18.wrap(Math.min(UFixed18.unwrap(a), UFixed18.unwrap(b))); } /** * @notice Returns the maximum of unsigned fixed-decimals `a` and `b` * @param a First unsigned fixed-decimal * @param b Second unsigned fixed-decimal * @return Maximum of `a` and `b` */ function max(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) { return UFixed18.wrap(Math.max(UFixed18.unwrap(a), UFixed18.unwrap(b))); } /** * @notice Converts the unsigned fixed-decimal into an integer, truncating any decimal portion * @param a Unsigned fixed-decimal * @return Truncated unsigned number */ function truncate(UFixed18 a) internal pure returns (uint256) { return UFixed18.unwrap(a) / BASE; } } library UFixed18StorageLib { function read(UFixed18Storage self) internal view returns (UFixed18 value) { assembly { value := sload(self) } } function store(UFixed18Storage self, UFixed18 value) internal { assembly { sstore(self, value) } } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "../number/types/UFixed18.sol"; /// @dev Stored boolean slot type BoolStorage is bytes32; using BoolStorageLib for BoolStorage global; /// @dev Stored uint256 slot type Uint256Storage is bytes32; using Uint256StorageLib for Uint256Storage global; /// @dev Stored int256 slot type Int256Storage is bytes32; using Int256StorageLib for Int256Storage global; /// @dev Stored address slot type AddressStorage is bytes32; using AddressStorageLib for AddressStorage global; /// @dev Stored bytes32 slot type Bytes32Storage is bytes32; using Bytes32StorageLib for Bytes32Storage global; /** * @title BoolStorageLib * @notice Library to manage storage and retrival of a boolean at a fixed storage slot */ library BoolStorageLib { /** * @notice Retrieves the stored value * @param self Storage slot * @return value Stored bool value */ function read(BoolStorage self) internal view returns (bool value) { assembly { value := sload(self) } } /** * @notice Stores the value at the specific slot * @param self Storage slot * @param value boolean value to store */ function store(BoolStorage self, bool value) internal { assembly { sstore(self, value) } } } /** * @title Uint256StorageLib * @notice Library to manage storage and retrival of an uint256 at a fixed storage slot */ library Uint256StorageLib { /** * @notice Retrieves the stored value * @param self Storage slot * @return value Stored uint256 value */ function read(Uint256Storage self) internal view returns (uint256 value) { assembly { value := sload(self) } } /** * @notice Stores the value at the specific slot * @param self Storage slot * @param value uint256 value to store */ function store(Uint256Storage self, uint256 value) internal { assembly { sstore(self, value) } } } /** * @title Int256StorageLib * @notice Library to manage storage and retrival of an int256 at a fixed storage slot */ library Int256StorageLib { /** * @notice Retrieves the stored value * @param self Storage slot * @return value Stored int256 value */ function read(Int256Storage self) internal view returns (int256 value) { assembly { value := sload(self) } } /** * @notice Stores the value at the specific slot * @param self Storage slot * @param value int256 value to store */ function store(Int256Storage self, int256 value) internal { assembly { sstore(self, value) } } } /** * @title AddressStorageLib * @notice Library to manage storage and retrival of an address at a fixed storage slot */ library AddressStorageLib { /** * @notice Retrieves the stored value * @param self Storage slot * @return value Stored address value */ function read(AddressStorage self) internal view returns (address value) { assembly { value := sload(self) } } /** * @notice Stores the value at the specific slot * @param self Storage slot * @param value address value to store */ function store(AddressStorage self, address value) internal { assembly { sstore(self, value) } } } /** * @title Bytes32StorageLib * @notice Library to manage storage and retrival of a bytes32 at a fixed storage slot */ library Bytes32StorageLib { /** * @notice Retrieves the stored value * @param self Storage slot * @return value Stored bytes32 value */ function read(Bytes32Storage self) internal view returns (bytes32 value) { assembly { value := sload(self) } } /** * @notice Stores the value at the specific slot * @param self Storage slot * @param value bytes32 value to store */ function store(Bytes32Storage self, bytes32 value) internal { assembly { sstore(self, value) } } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "../../number/types/UFixed18.sol"; /// @dev Token18 type Token18 is address; using Token18Lib for Token18 global; type Token18Storage is bytes32; using Token18StorageLib for Token18Storage global; /** * @title Token18Lib * @notice Library to manage 18-decimal ERC20s that is compliant with the fixed-decimal types. * @dev Maintains significant gas savings over other Token implementations since no conversion take place */ library Token18Lib { using SafeERC20 for IERC20; Token18 public constant ZERO = Token18.wrap(address(0)); /** * @notice Returns whether a token is the zero address * @param self Token to check for * @return Whether the token is the zero address */ function isZero(Token18 self) internal pure returns (bool) { return Token18.unwrap(self) == Token18.unwrap(ZERO); } /** * @notice Returns whether the two tokens are equal * @param a First token to compare * @param b Second token to compare * @return Whether the two tokens are equal */ function eq(Token18 a, Token18 b) internal pure returns (bool) { return Token18.unwrap(a) == Token18.unwrap(b); } /** * @notice Approves `grantee` to spend infinite tokens from the caller * @param self Token to transfer * @param grantee Address to allow spending */ function approve(Token18 self, address grantee) internal { IERC20(Token18.unwrap(self)).safeApprove(grantee, type(uint256).max); } /** * @notice Approves `grantee` to spend `amount` tokens from the caller * @dev There are important race conditions to be aware of when using this function with values other than 0. This will revert if moving from non-zero to non-zero amounts See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/a55b7d13722e7ce850b626da2313f3e66ca1d101/contracts/token/ERC20/IERC20.sol#L57 * @param self Token to transfer * @param grantee Address to allow spending * @param amount Amount of tokens to approve to spend */ function approve(Token18 self, address grantee, UFixed18 amount) internal { IERC20(Token18.unwrap(self)).safeApprove(grantee, UFixed18.unwrap(amount)); } /** * @notice Transfers all held tokens from the caller to the `recipient` * @param self Token to transfer * @param recipient Address to receive the tokens */ function push(Token18 self, address recipient) internal { push(self, recipient, balanceOf(self, address(this))); } /** * @notice Transfers `amount` tokens from the caller to the `recipient` * @param self Token to transfer * @param recipient Address to transfer tokens to * @param amount Amount of tokens to transfer */ function push(Token18 self, address recipient, UFixed18 amount) internal { IERC20(Token18.unwrap(self)).safeTransfer(recipient, UFixed18.unwrap(amount)); } /** * @notice Transfers `amount` tokens from the `benefactor` to the caller * @dev Reverts if trying to pull Ether * @param self Token to transfer * @param benefactor Address to transfer tokens from * @param amount Amount of tokens to transfer */ function pull(Token18 self, address benefactor, UFixed18 amount) internal { IERC20(Token18.unwrap(self)).safeTransferFrom(benefactor, address(this), UFixed18.unwrap(amount)); } /** * @notice Transfers `amount` tokens from the `benefactor` to `recipient` * @dev Reverts if trying to pull Ether * @param self Token to transfer * @param benefactor Address to transfer tokens from * @param recipient Address to transfer tokens to * @param amount Amount of tokens to transfer */ function pullTo(Token18 self, address benefactor, address recipient, UFixed18 amount) internal { IERC20(Token18.unwrap(self)).safeTransferFrom(benefactor, recipient, UFixed18.unwrap(amount)); } /** * @notice Returns the name of the token * @param self Token to check for * @return Token name */ function name(Token18 self) internal view returns (string memory) { return IERC20Metadata(Token18.unwrap(self)).name(); } /** * @notice Returns the symbol of the token * @param self Token to check for * @return Token symbol */ function symbol(Token18 self) internal view returns (string memory) { return IERC20Metadata(Token18.unwrap(self)).symbol(); } /** * @notice Returns the `self` token balance of the caller * @param self Token to check for * @return Token balance of the caller */ function balanceOf(Token18 self) internal view returns (UFixed18) { return balanceOf(self, address(this)); } /** * @notice Returns the `self` token balance of `account` * @param self Token to check for * @param account Account to check * @return Token balance of the account */ function balanceOf(Token18 self, address account) internal view returns (UFixed18) { return UFixed18.wrap(IERC20(Token18.unwrap(self)).balanceOf(account)); } } library Token18StorageLib { function read(Token18Storage self) internal view returns (Token18 value) { assembly { value := sload(self) } } function store(Token18Storage self, Token18 value) internal { assembly { sstore(self, value) } } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; import "../../number/types/UFixed18.sol"; /// @dev Token6 type Token6 is address; using Token6Lib for Token6 global; type Token6Storage is bytes32; using Token6StorageLib for Token6Storage global; /** * @title Token6Lib * @notice Library to manage 6-decimal ERC20s that is compliant with the fixed-decimal types. * @dev Automatically converts from Base-6 token amounts to Base-18 UFixed18 amounts, with optional rounding */ library Token6Lib { using SafeERC20 for IERC20; Token6 public constant ZERO = Token6.wrap(address(0)); uint256 private constant OFFSET = 1e12; /** * @notice Returns whether a token is the zero address * @param self Token to check for * @return Whether the token is the zero address */ function isZero(Token6 self) internal pure returns (bool) { return Token6.unwrap(self) == Token6.unwrap(ZERO); } /** * @notice Returns whether the two tokens are equal * @param a First token to compare * @param b Second token to compare * @return Whether the two tokens are equal */ function eq(Token6 a, Token6 b) internal pure returns (bool) { return Token6.unwrap(a) == Token6.unwrap(b); } /** * @notice Approves `grantee` to spend infinite tokens from the caller * @param self Token to transfer * @param grantee Address to allow spending */ function approve(Token6 self, address grantee) internal { IERC20(Token6.unwrap(self)).safeApprove(grantee, type(uint256).max); } /** * @notice Approves `grantee` to spend `amount` tokens from the caller * @dev There are important race conditions to be aware of when using this function with values other than 0. This will revert if moving from non-zero to non-zero amounts See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/a55b7d13722e7ce850b626da2313f3e66ca1d101/contracts/token/ERC20/IERC20.sol#L57 * @param self Token to transfer * @param grantee Address to allow spending * @param amount Amount of tokens to approve to spend */ function approve(Token6 self, address grantee, UFixed18 amount) internal { IERC20(Token6.unwrap(self)).safeApprove(grantee, toTokenAmount(amount, false)); } /** * @notice Approves `grantee` to spend `amount` tokens from the caller * @dev There are important race conditions to be aware of when using this function with values other than 0. This will revert if moving from non-zero to non-zero amounts See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/a55b7d13722e7ce850b626da2313f3e66ca1d101/contracts/token/ERC20/IERC20.sol#L57 * @param self Token to transfer * @param grantee Address to allow spending * @param amount Amount of tokens to approve to spend * @param roundUp Whether to round decimal token amount up to the next unit */ function approve(Token6 self, address grantee, UFixed18 amount, bool roundUp) internal { IERC20(Token6.unwrap(self)).safeApprove(grantee, toTokenAmount(amount, roundUp)); } /** * @notice Transfers all held tokens from the caller to the `recipient` * @param self Token to transfer * @param recipient Address to receive the tokens */ function push(Token6 self, address recipient) internal { push(self, recipient, balanceOf(self, address(this))); } /** * @notice Transfers `amount` tokens from the caller to the `recipient` * @param self Token to transfer * @param recipient Address to transfer tokens to * @param amount Amount of tokens to transfer */ function push(Token6 self, address recipient, UFixed18 amount) internal { IERC20(Token6.unwrap(self)).safeTransfer(recipient, toTokenAmount(amount, false)); } /** * @notice Transfers `amount` tokens from the caller to the `recipient` * @param self Token to transfer * @param recipient Address to transfer tokens to * @param amount Amount of tokens to transfer * @param roundUp Whether to round decimal token amount up to the next unit */ function push(Token6 self, address recipient, UFixed18 amount, bool roundUp) internal { IERC20(Token6.unwrap(self)).safeTransfer(recipient, toTokenAmount(amount, roundUp)); } /** * @notice Transfers `amount` tokens from the `benefactor` to the caller * @dev Reverts if trying to pull Ether * @param self Token to transfer * @param benefactor Address to transfer tokens from * @param amount Amount of tokens to transfer */ function pull(Token6 self, address benefactor, UFixed18 amount) internal { IERC20(Token6.unwrap(self)).safeTransferFrom(benefactor, address(this), toTokenAmount(amount, false)); } /** * @notice Transfers `amount` tokens from the `benefactor` to the caller * @dev Reverts if trying to pull Ether * @param self Token to transfer * @param benefactor Address to transfer tokens from * @param amount Amount of tokens to transfer * @param roundUp Whether to round decimal token amount up to the next unit */ function pull(Token6 self, address benefactor, UFixed18 amount, bool roundUp) internal { IERC20(Token6.unwrap(self)).safeTransferFrom(benefactor, address(this), toTokenAmount(amount, roundUp)); } /** * @notice Transfers `amount` tokens from the `benefactor` to `recipient` * @dev Reverts if trying to pull Ether * @param self Token to transfer * @param benefactor Address to transfer tokens from * @param recipient Address to transfer tokens to * @param amount Amount of tokens to transfer */ function pullTo(Token6 self, address benefactor, address recipient, UFixed18 amount) internal { IERC20(Token6.unwrap(self)).safeTransferFrom(benefactor, recipient, toTokenAmount(amount, false)); } /** * @notice Transfers `amount` tokens from the `benefactor` to `recipient` * @dev Reverts if trying to pull Ether * @param self Token to transfer * @param benefactor Address to transfer tokens from * @param recipient Address to transfer tokens to * @param amount Amount of tokens to transfer * @param roundUp Whether to round decimal token amount up to the next unit */ function pullTo(Token6 self, address benefactor, address recipient, UFixed18 amount, bool roundUp) internal { IERC20(Token6.unwrap(self)).safeTransferFrom(benefactor, recipient, toTokenAmount(amount, roundUp)); } /** * @notice Returns the name of the token * @param self Token to check for * @return Token name */ function name(Token6 self) internal view returns (string memory) { return IERC20Metadata(Token6.unwrap(self)).name(); } /** * @notice Returns the symbol of the token * @param self Token to check for * @return Token symbol */ function symbol(Token6 self) internal view returns (string memory) { return IERC20Metadata(Token6.unwrap(self)).symbol(); } /** * @notice Returns the `self` token balance of the caller * @param self Token to check for * @return Token balance of the caller */ function balanceOf(Token6 self) internal view returns (UFixed18) { return balanceOf(self, address(this)); } /** * @notice Returns the `self` token balance of `account` * @param self Token to check for * @param account Account to check * @return Token balance of the account */ function balanceOf(Token6 self, address account) internal view returns (UFixed18) { return fromTokenAmount(IERC20(Token6.unwrap(self)).balanceOf(account)); } /** * @notice Converts the unsigned fixed-decimal amount into the token amount according to * it's defined decimals * @dev Provides the ability to "round up" the token amount which is useful in situations where * are swapping one token for another and don't want to give away "free" units due to rounding * errors in the favor of the user. * @param amount Amount to convert * @param roundUp Whether to round decimal token amount up to the next unit * @return Normalized token amount */ function toTokenAmount(UFixed18 amount, bool roundUp) private pure returns (uint256) { return roundUp ? Math.ceilDiv(UFixed18.unwrap(amount), OFFSET) : UFixed18.unwrap(amount) / OFFSET; } /** * @notice Converts the token amount into the unsigned fixed-decimal amount according to * it's defined decimals * @param amount Token amount to convert * @return Normalized unsigned fixed-decimal amount */ function fromTokenAmount(uint256 amount) private pure returns (UFixed18) { return UFixed18.wrap(amount * OFFSET); } } library Token6StorageLib { function read(Token6Storage self) internal view returns (Token6 value) { assembly { value := sload(self) } } function store(Token6Storage self, Token6 value) internal { assembly { sstore(self, value) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol) pragma solidity ^0.8.0; /** * @dev This is the interface that {BeaconProxy} expects of its beacon. */ interface IBeacon { /** * @dev Must return an address that can be used as a delegate call target. * * {BeaconProxy} will check that this address is a contract. */ function implementation() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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); /** * @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 `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, 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 changes 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. * * IMPORTANT: 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 `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a / b + (a % b == 0 ? 0 : 1); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
{ "evmVersion": "london", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 200 }, "remappings": [], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
[{"inputs":[{"internalType":"Token18","name":"asset_","type":"address"},{"internalType":"contract IController","name":"controller_","type":"address"},{"internalType":"contract IProduct","name":"long_","type":"address"},{"internalType":"contract IProduct","name":"short_","type":"address"},{"internalType":"UFixed18","name":"targetLeverage_","type":"uint256"},{"internalType":"UFixed18","name":"maxCollateral_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BalancedVaultDepositLimitExceeded","type":"error"},{"inputs":[],"name":"BalancedVaultRedemptionLimitExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"Fixed18OverflowError","type":"error"},{"inputs":[{"internalType":"int256","name":"value","type":"int256"}],"name":"UFixed18UnderflowError","type":"error"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"}],"name":"UInitializableAlreadyInitializedError","type":"error"},{"inputs":[],"name":"UInitializableNotInitializingError","type":"error"},{"inputs":[],"name":"UInitializableZeroVersionError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"UFixed18","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"UFixed18","name":"assets","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IProduct","name":"product","type":"address"},{"indexed":false,"internalType":"UFixed18","name":"targetCollateral","type":"uint256"}],"name":"CollateralUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"version","type":"uint256"},{"indexed":false,"internalType":"UFixed18","name":"assets","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"version","type":"uint256"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IProduct","name":"product","type":"address"},{"indexed":false,"internalType":"UFixed18","name":"targetPosition","type":"uint256"}],"name":"PositionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"version","type":"uint256"},{"indexed":false,"internalType":"UFixed18","name":"shares","type":"uint256"}],"name":"Redemption","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"UFixed18","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"UFixed18","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"Token18","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract ICollateral","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"contract IController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"UFixed18","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"UFixed18","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"UFixed18","name":"assets","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"long","outputs":[{"internalType":"contract IProduct","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxCollateral","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"UFixed18","name":"shares","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"short","outputs":[{"internalType":"contract IProduct","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"targetLeverage","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalUnclaimed","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"UFixed18","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"UFixed18","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"unclaimed","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6101606040523480156200001257600080fd5b5060405162003b3838038062003b388339810160408190526200003591620000f5565b6001600160a01b0380871661014052851660808190526040805163d8dfeb4560e01b8152905163d8dfeb45916004808201926020929091908290030181865afa15801562000087573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ad919062000171565b6001600160a01b0390811660a05293841660c0529190921660e052610100919091526101205250620001989050565b6001600160a01b0381168114620000f257600080fd5b50565b60008060008060008060c087890312156200010f57600080fd5b86516200011c81620000dc565b60208801519096506200012f81620000dc565b60408801519095506200014281620000dc565b60608801519094506200015581620000dc565b809350506080870151915060a087015190509295509295509295565b6000602082840312156200018457600080fd5b81516200019181620000dc565b9392505050565b60805160a05160c05160e0516101005161012051610140516138036200033560003960008181610300015281816106d20152818161080f015281816109b801526114720152600081816102a30152818161168c01526116c10152600081816103cf01526122a90152600081816102d901528181610cb201528181610ffc0152818161114f015281816113da01528181611b5901528181611bd601528181611d1601528181611fb70152818161201f015281816123040152818161248201526125be01526000818161021101528181610c3001528181610f6d0152818161109a01528181611321015281816119cf01528181611a4c01528181611c9f01528181611d9301528181611fd801528181611ffe015281816120490152818161209401528181612215015281816122da015281816123c701526125300152600081816103f601528181610831015281816110c50152818161117d0152818161135001528181611402015281816123f2015281816124aa015281816128da015261298c01526000818161045b01528181611f14015261216601526138036000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c8063505bd3da116100f9578063c96f14b811610097578063d905777e11610071578063d905777e14610418578063dd62ed3e1461042b578063f77c479114610456578063fff6cae91461047d57600080fd5b8063c96f14b8146103c2578063d6c946ea146103ca578063d8dfeb45146103f157600080fd5b80637bde82f2116100d35780637bde82f21461038157806395d89b4114610394578063a9059cbb1461039c578063c6e6f592146103af57600080fd5b8063505bd3da146103485780636e553f651461035b57806370a082311461036e57600080fd5b806323b872dd1161016657806338bea4cb1161014057806338bea4cb146102d457806338d52e0f146102fb578063402d267d146103225780634cd88b761461033557600080fd5b806323b872dd1461028b578063251f47c11461029e578063313ce567146102c557600080fd5b806307bfce37116101a257806307bfce371461020c578063095ea7b31461024b57806318160ddd1461026e5780631e83409a1461027657600080fd5b806301e1d114146101c957806306fdde03146101e457806307a2d13a146101f9575b600080fd5b6101d1610485565b6040519081526020015b60405180910390f35b6101ec6104a4565b6040516101db9190613198565b6101d16102073660046131cb565b610532565b6102337f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101db565b61025e610259366004613200565b61056f565b60405190151581526020016101db565b6101d16105dc565b61028961028436600461322a565b6105f5565b005b61025e610299366004613245565b610704565b6101d17f000000000000000000000000000000000000000000000000000000000000000081565b604051601281526020016101db565b6102337f000000000000000000000000000000000000000000000000000000000000000081565b6102337f000000000000000000000000000000000000000000000000000000000000000081565b6101d161033036600461322a565b610731565b610289610343366004613324565b61074a565b6101d161035636600461322a565b6108b2565b610289610369366004613388565b6108cb565b6101d161037c36600461322a565b6109ef565b61028961038f366004613388565b610a08565b6101ec610b22565b61025e6103aa366004613200565b610b2f565b6101d16103bd3660046131cb565b610b50565b6101d1610b86565b6101d17f000000000000000000000000000000000000000000000000000000000000000081565b6102337f000000000000000000000000000000000000000000000000000000000000000081565b6101d161042636600461322a565b610b9f565b6101d16104393660046133b4565b600260209081526000928352604080842090915290825290205481565b6102337f000000000000000000000000000000000000000000000000000000000000000081565b610289610bc4565b6000806104926000610be1565b50905061049e81610df1565b91505090565b600080546104b1906133de565b80601f01602080910402602001604051908101604052809291908181526020018280546104dd906133de565b801561052a5780601f106104ff5761010080835404028352916020019161052a565b820191906000526020600020905b81548152906001019060200180831161050d57829003601f168201915b505050505081565b60008061053f6000610be1565b50905061054b81610df1565b61055482610e55565b604083015260208201526105688184610e84565b9392505050565b3360008181526002602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906105ca9086815260200190565b60405180910390a35060015b92915050565b6000806105e96000610be1565b50905061049e81610e55565b600061060082610eb3565b506001600160a01b038316600090815260056020526040812080546006549290915591925061062f81836112f8565b6006556040518281526001600160a01b0385169033907f70eb43c4a8ae8c40502dcf22436c509c28d6ff421cf07c491be56984bd9870689060200160405180910390a3816000808061067f611304565b91945092509050600061069c8261069686866114aa565b906114aa565b90506106a881876114b6565b156106bb576106b88582886114ca565b94505b6106c588866114e1565b6106f96001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168a876114f8565b505050505050505050565b600061070f84610eb3565b505061071c84338461150c565b6107278484846115a9565b5060019392505050565b60008061073e6000610be1565b5090506105688161165c565b6001806107757f5db5abc19987c2b3729df7961b62b6bb0bae886dd47e3ce25bb3a3af34c6d80b5490565b1061079b57604051631e7a9d9560e01b8152600481018290526024015b60405180910390fd5b6107c47f5db5abc19987c2b3729df7961b62b6bb0bae886dd47e3ce25bb3a3af34c6d80b829055565b60017fad57d7911b7e3d6c3c79a68ba909a7f4ba41f9485e5207b12dee0d0c6af5398c5560006107f4848261345e565b506001610801838261345e565b506108556001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000006116e6565b60007fad57d7911b7e3d6c3c79a68ba909a7f4ba41f9485e5207b12dee0d0c6af5398c556040518181527fbe9b076dc5b65990cca9dd9d7366682482e7817a6f6bc7f4faf4dc32af497f32906020015b60405180910390a1505050565b6000806108be83610be1565b91505061056881846116fc565b60006108d682610eb3565b5090506108ec6108e58261165c565b8490611782565b1561090a57604051636726139b60e01b815260040160405180910390fd5b60075461091790846114aa565b60075580516009556001600160a01b0382166000908152600a602052604090205461094290846114aa565b6001600160a01b0383166000818152600a60209081526040808320949094558451600c8252918490209190915583518351908152908101869052909133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a36109df6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163385611798565b6109ea8160006114e1565b505050565b6000806109fb83610be1565b91505061056881846117ad565b336001600160a01b03821614610a2357610a2381338461150c565b600080610a2f83610eb3565b91509150610a48610a41838386611833565b8590611782565b15610a66576040516322be0ced60e11b815260040160405180910390fd5b600854610a7390856114aa565b60085581516009556001600160a01b0383166000908152600b6020526040902054610a9e90856114aa565b6001600160a01b0384166000818152600b60209081526040808320949094558551600c8252918490209190915584518351908152908101879052909133917fa28d80c9910787c0c058ed9b50c577f1389264bf61563fa45529e0771976f562910160405180910390a3610b118385611855565b610b1c8260006114e1565b50505050565b600180546104b1906133de565b6000610b3a33610eb3565b5050610b473384846115a9565b50600192915050565b600080610b5d6000610be1565b509050610b6981610df1565b610b7282610e55565b6040830152602082015261056881846118e7565b600080610b936000610be1565b50905061049e81611916565b6000806000610bad84610be1565b91509150610bbc828286611833565b949350505050565b6000610bd06000610eb3565b509050610bde8160006114e1565b50565b610c0560405180606001604052806000815260200160008152602001600081525090565b610c2960405180606001604052806000815260200160008152602001600081525090565b6000610d377f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb0919061351e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d32919061351e565b611945565b90506040518060600160405280828152602001610d5560095461195b565b8152602001610d756009546000908152600d602052604090206002015490565b8152506040518060600160405280838152602001610db7600c6000896001600160a01b03166001600160a01b031681526020019081526020016000205461195b565b81526001600160a01b03969096166000908152600c60209081526040808320548352600d8252909120600201549601959095529492505050565b600080600080610dff611304565b91945092509050600080610e178361069687876114aa565b610e266007546106968a611916565b9092509050610e358282611782565b610e40576000610e4a565b610e4a82826112f8565b979650505050505050565b6000600954826000015103610e6c57505060045490565b6105d6610e7b836007546118e7565b600454906114aa565b6000610e9283604001511590565b15610e9e5750806105d6565b602083015160408401516105689184916114ca565b610ed760405180606001604052806000815260200160008152602001600081525090565b610efb60405180606001604052806000815260200160008152602001600081525090565b610f0483611c42565b6009548251929450909250101561124357610f32610f2d600454610f2785610e55565b906112f8565b611e07565b610f3b82611916565b6006556000600781905560085581516009556040805160c081019182905263b7648fb960e01b9091523060c4820152807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b7648fb960e483016040805180830381865afa158015610fba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fde9190613586565b51815260405163b7648fb960e01b81523060048201526020909101907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b7648fb9906024016040805180830381865afa15801561104a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106e9190613586565b51815260048054602083015260408051636610c76760e11b815230928101929092526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660248401529201917f0000000000000000000000000000000000000000000000000000000000000000169063cc218ece90604401602060405180830381865afa15801561110c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611130919061351e565b8152604051636610c76760e11b81523060048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660248301526020909201917f0000000000000000000000000000000000000000000000000000000000000000169063cc218ece90604401602060405180830381865afa1580156111c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e8919061351e565b81526020016111f684610df1565b905282516000908152600d602090815260409182902083518155908301516001820155908201516002820155606082015160038201556080820151600482015560a0909101516005909101555b6001600160a01b0383161580159061127357506001600160a01b0383166000908152600c60205260409020548151115b156112f3576001600160a01b0383166000908152600360205260409020546112aa9084906112a590610f2785846117ad565b611e1a565b6112b481846116fc565b6001600160a01b038416600090815260056020908152604080832093909355600a8152828220829055600b81528282208290558351600c909152919020555b915091565b600061056882846135b8565b604051636610c76760e11b81523060048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166024830152600091829182917f00000000000000000000000000000000000000000000000000000000000000009091169063cc218ece90604401602060405180830381865afa158015611399573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113bd919061351e565b604051636610c76760e11b81523060048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063cc218ece90604401602060405180830381865afa158015611449573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146d919061351e565b61149f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611e8e565b925092509250909192565b600061056882846135cb565b60006114c28383611e9a565b159392505050565b6000816114d784866135de565b610bbc919061360b565b6114ea81611ecf565b6114f482826120db565b5050565b6109ea6001600160a01b0384168383612331565b6001600160a01b0380841660009081526002602090815260408083209386168352929052205461153e90600019612394565b1561154857505050565b6001600160a01b0380841660009081526002602090815260408083209386168352929052205461157890826112f8565b6001600160a01b03938416600090815260026020908152604080832095909616825293909352929091209190915550565b6001600160a01b0383166000908152600360205260409020546115cc90826112f8565b6001600160a01b0380851660009081526003602052604080822093909355908416815220546115fb90826114aa565b6001600160a01b0380841660008181526003602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061164f9085815260200190565b60405180910390a3505050565b6000611667826123aa565b1561167457506000919050565b600061168560075461069685610df1565b90506116b17f000000000000000000000000000000000000000000000000000000000000000082611782565b6116bc576000610568565b6105687f0000000000000000000000000000000000000000000000000000000000000000826112f8565b6114f46001600160a01b03831682600019612650565b6001600160a01b0381166000908152600c602052604081205483510361173b57506001600160a01b0381166000908152600560205260409020546105d6565b6001600160a01b0382166000908152600b602052604090205461056890611763908590610e84565b6001600160a01b038416600090815260056020526040902054906114aa565b600061178e8383611e9a565b6002149392505050565b6109ea6001600160a01b038416833084612765565b6001600160a01b0381166000908152600c60205260408120548351036117ec57506001600160a01b0381166000908152600360205260409020546105d6565b6001600160a01b0382166000908152600a6020526040902054610568906118149085906118e7565b6001600160a01b038416600090815260036020526040902054906114aa565b600061183e846123aa565b1561184b57506000610568565b610bbc83836117ad565b6001600160a01b03821660009081526003602052604090205461187890826112f8565b6001600160a01b03831660009081526003602052604090205560045461189e90826112f8565b6004556040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b60006118f583602001511590565b156119015750806105d6565b604083015160208401516105689184916114ca565b600060095482600001510361192d57505060065490565b6105d661193c83600854610e84565b600654906114aa565b60008183106119545781610568565b5090919050565b600080611ae7611999611978670de0b6b3a764000060001961361f565b6000868152600d60205260409020600301546119939061279d565b906127ca565b6000858152600d6020526040902054611ae1906119b59061279d565b60405163083fa70f60e21b815260048101889052611993907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906320fe9c3c906024016040805180830381865afa158015611a1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a419190613537565b516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166320fe9c3c611a7c8b60016135cb565b6040518263ffffffff1660e01b8152600401611a9a91815260200190565b6040805180830381865afa158015611ab6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ada9190613537565b51906127e9565b906127f5565b90506000611c06611b20611b05670de0b6b3a764000060001961361f565b6000878152600d60205260409020600401546119939061279d565b6000868152600d6020526040902060010154611ae190611b3f9061279d565b60405163083fa70f60e21b815260048101899052611993907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906320fe9c3c906024016040805180830381865afa158015611ba7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bcb9190613537565b516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166320fe9c3c611a7c8c60016135cb565b9050610bbc611c3d6000611ae184611c3787611c37600d60008d81526020019081526020016000206005015461279d565b90612801565b61280d565b611c6660405180606001604052806000815260200160008152602001600081525090565b611c8a60405180606001604052806000815260200160008152602001600081525090565b60405163f667f89760e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063f667f89790602401600060405180830381600087803b158015611ceb57600080fd5b505af1158015611cff573d6000803e3d6000fd5b505060405163f667f89760e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316925063f667f8979150602401600060405180830381600087803b158015611d6457600080fd5b505af1158015611d78573d6000803e3d6000fd5b5050604051630472405960e51b8152306004820152600092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169150638e480b2090602401602060405180830381865afa158015611de3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d37919061351e565b600454611e1490826114aa565b60045550565b6001600160a01b038216600090815260036020526040902054611e3d90826114aa565b6001600160a01b0383166000818152600360205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906118db9085815260200190565b60006105d68230612834565b6000828280821115611eb1576002925050506105d6565b80821015611ec4576000925050506105d6565b506001949350505050565b6000806000611edc611304565b919450925090506000611ef785610f278461069688886114aa565b90506000611f0d82671bc16d674ec800006128a2565b9050611f9b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ba2de9bc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f94919061351e565b82906114b6565b15611fa4575060005b600080611fb18787611782565b611ffc577f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000061203f565b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000005b9150915061208e827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316146120865787612088565b885b856128c1565b6120d1817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316146120865787612088565b5050505050505050565b60006120ea82610f2785610df1565b9050600061210c6121086008546004546114aa90919063ffffffff16565b1590565b612146576121416121386004546121306008546004546114aa90919063ffffffff16565b8591906114ca565b600754906114aa565b612153565b60075461215390836114aa565b90506121ec611f94671bc16d674ec800007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ba2de9bc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121e6919061351e565b90612a64565b156121f5575060005b8351604051637ece075d60e01b81526004810191909152600090612291907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690637ece075d90602401606060405180830381865afa158015612264573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612288919061364f565b60400151612a79565b905060006122d3671bc16d674ec800006122cd8481877f0000000000000000000000000000000000000000000000000000000000000000612a64565b906128a2565b90506122ff7f000000000000000000000000000000000000000000000000000000000000000082612a84565b6123297f000000000000000000000000000000000000000000000000000000000000000082612a84565b505050505050565b6040516001600160a01b0383166024820152604481018290526109ea90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612e32565b60006123a08383611e9a565b6001149392505050565b60405163416f9cef60e11b81523060048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660248301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906382df39de90604401602060405180830381865afa15801561243b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061245f91906136ab565b80612515575060405163416f9cef60e11b81523060048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660248301527f000000000000000000000000000000000000000000000000000000000000000016906382df39de90604401602060405180830381865afa1580156124f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061251591906136ab565b806125a35750604051630b194df360e31b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906358ca6f9890602401602060405180830381865afa15801561257f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125a391906136ab565b806126315750604051630b194df360e31b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906358ca6f9890602401602060405180830381865afa15801561260d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061263191906136ab565b806105d657506040820151151580156105d657506020820151156105d6565b8015806126ca5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa1580156126a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126c8919061351e565b155b6127355760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610792565b6040516001600160a01b0383166024820152604481018290526109ea90849063095ea7b360e01b9060640161235d565b6040516001600160a01b0380851660248301528316604482015260648101829052610b1c9085906323b872dd60e01b9060840161235d565b6000816001600160ff1b038111156105d65760405162a07eb560e01b815260048101829052602401610792565b6000670de0b6b3a76400006127df838561361f565b61056891906136cd565b600061056882846136fb565b60006105688383612f04565b60006105688284613722565b600081818112156105d65760405163280f944f60e11b815260048101829052602401610792565b6040516370a0823160e01b81526001600160a01b038281166004830152600091908416906370a0823190602401602060405180830381865afa15801561287e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610568919061351e565b6000816128b7670de0b6b3a7640000856135de565b610568919061360b565b6128cb8282611782565b15612973576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663c3b35a7e308561290b86866112f8565b6040516001600160e01b031960e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b15801561295a57600080fd5b505af115801561296e573d6000803e3d6000fd5b505050505b61297d82826114b6565b15612a25576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663f213159c30856129bd85876112f8565b6040516001600160e01b031960e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b158015612a0c57600080fd5b505af1158015612a20573d6000803e3d6000fd5b505050505b604080516001600160a01b0385168152602081018390527fdf43c689abc8d83e86b74634bbe0ba590e2ecb34f3f432a612c6edef3e532cbd91016108a5565b6000670de0b6b3a76400006128b783856135de565b60006105d682612f14565b604051631e0c6fb960e01b8152306004820152600090612b61906001600160a01b03851690631e0c6fb99060240160a060405180830381865afa158015612acf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612af3919061374a565b60405163b7648fb960e01b81523060048201526001600160a01b0386169063b7648fb9906024015b6040805180830381865afa158015612b37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b5b9190613586565b90612f2b565b6000015190506000612c5d846001600160a01b03166359ea287d6040518163ffffffff1660e01b815260040160a060405180830381865afa158015612baa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bce919061374a565b856001600160a01b0316639a427d03876001600160a01b031663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3f919061351e565b6040518263ffffffff1660e01b8152600401612b1b91815260200190565b6000015190506000846001600160a01b031663193775676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ca3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cc7919061351e565b90506000612cd58284611782565b612ce0576000612cea565b612cea82846112f8565b9050612cf685856114b6565b15612d65576001600160a01b0386166359218fe9612d1486886112f8565b6040518263ffffffff1660e01b8152600401612d3291815260200190565b600060405180830381600087803b158015612d4c57600080fd5b505af1158015612d60573d6000803e3d6000fd5b505050505b612d6f8585611782565b15612de8576001600160a01b03861663d7d7d6b8612d9783612d9189896112f8565b90612f5a565b6040518263ffffffff1660e01b8152600401612db591815260200190565b600060405180830381600087803b158015612dcf57600080fd5b505af1158015612de3573d6000803e3d6000fd5b505050505b604080516001600160a01b0388168152602081018790527f814d2340073b1f05a68bb3f6aab82bfc4eea78f35f7c2332caddf0c35865331e910160405180910390a1505050505050565b6000612e87826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612f669092919063ffffffff16565b8051909150156109ea5780806020019051810190612ea591906136ab565b6109ea5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610792565b6000818312156119545781610568565b600080821215612f2757816000036105d6565b5090565b6040805180820190915260008082526020820152610568612f50848460200151612f75565b8360400151612fcc565b60006105688383611945565b6060610bbc848460008561301a565b604080518082019091526000808252602082015260408051808201909152825184518291612fa391906114aa565b8152602001612fc3846020015186602001516114aa90919063ffffffff16565b90529392505050565b604080518082019091526000808252602082015260408051808201909152825184518291612ffa91906112f8565b8152602001612fc3846020015186602001516112f890919063ffffffff16565b60608247101561307b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610792565b6001600160a01b0385163b6130d25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610792565b600080866001600160a01b031685876040516130ee91906137b1565b60006040518083038185875af1925050503d806000811461312b576040519150601f19603f3d011682016040523d82523d6000602084013e613130565b606091505b5091509150610e4a8282866060831561314a575081610568565b82511561315a5782518084602001fd5b8160405162461bcd60e51b81526004016107929190613198565b60005b8381101561318f578181015183820152602001613177565b50506000910152565b60208152600082518060208401526131b7816040850160208701613174565b601f01601f19169190910160400192915050565b6000602082840312156131dd57600080fd5b5035919050565b80356001600160a01b03811681146131fb57600080fd5b919050565b6000806040838503121561321357600080fd5b61321c836131e4565b946020939093013593505050565b60006020828403121561323c57600080fd5b610568826131e4565b60008060006060848603121561325a57600080fd5b613263846131e4565b9250613271602085016131e4565b9150604084013590509250925092565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126132a857600080fd5b813567ffffffffffffffff808211156132c3576132c3613281565b604051601f8301601f19908116603f011681019082821181831017156132eb576132eb613281565b8160405283815286602085880101111561330457600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806040838503121561333757600080fd5b823567ffffffffffffffff8082111561334f57600080fd5b61335b86838701613297565b9350602085013591508082111561337157600080fd5b5061337e85828601613297565b9150509250929050565b6000806040838503121561339b57600080fd5b823591506133ab602084016131e4565b90509250929050565b600080604083850312156133c757600080fd5b6133d0836131e4565b91506133ab602084016131e4565b600181811c908216806133f257607f821691505b60208210810361341257634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156109ea57600081815260208120601f850160051c8101602086101561343f5750805b601f850160051c820191505b818110156123295782815560010161344b565b815167ffffffffffffffff81111561347857613478613281565b61348c8161348684546133de565b84613418565b602080601f8311600181146134c157600084156134a95750858301515b600019600386901b1c1916600185901b178555612329565b600085815260208120601f198616915b828110156134f0578886015182559484019460019091019084016134d1565b508582101561350e5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561353057600080fd5b5051919050565b60006040828403121561354957600080fd5b6040516040810181811067ffffffffffffffff8211171561356c5761356c613281565b604052825181526020928301519281019290925250919050565b60006040828403121561359857600080fd5b6105688383613537565b634e487b7160e01b600052601160045260246000fd5b818103818111156105d6576105d66135a2565b808201808211156105d6576105d66135a2565b80820281158282048414176105d6576105d66135a2565b634e487b7160e01b600052601260045260246000fd5b60008261361a5761361a6135f5565b500490565b80820260008212600160ff1b8414161561363b5761363b6135a2565b81810583148215176105d6576105d66135a2565b60006060828403121561366157600080fd5b6040516060810181811067ffffffffffffffff8211171561368457613684613281565b80604052508251815260208301516020820152604083015160408201528091505092915050565b6000602082840312156136bd57600080fd5b8151801515811461056857600080fd5b6000826136dc576136dc6135f5565b600160ff1b8214600019841416156136f6576136f66135a2565b500590565b818103600083128015838313168383128216171561371b5761371b6135a2565b5092915050565b8082018281126000831280158216821582161715613742576137426135a2565b505092915050565b600060a0828403121561375c57600080fd5b6040516060810181811067ffffffffffffffff8211171561377f5761377f613281565b604052825181526137938460208501613537565b60208201526137a58460608501613537565b60408201529392505050565b600082516137c3818460208701613174565b919091019291505056fea264697066735822122011008e21f615565cf4877c21ee64eac089b41a434e9088e497e50e88da5959ee64736f6c6343000811003300000000000000000000000052c64b8998eb7c80b6f526e99e29abdcc86b841b000000000000000000000000a59ef0208418559770a48d7ae4f260a28763167b0000000000000000000000001cd33f4e6edeee8263aa07924c2760cf2ec8aad00000000000000000000000004243b34374cfb0a12f184b92f52035d03d4f705600000000000000000000000000000000000000000000000022b1c8c1227a0000000000000000000000000000000000000000000000027b46536c66c8e3000000
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000052c64b8998eb7c80b6f526e99e29abdcc86b841b000000000000000000000000a59ef0208418559770a48d7ae4f260a28763167b0000000000000000000000001cd33f4e6edeee8263aa07924c2760cf2ec8aad00000000000000000000000004243b34374cfb0a12f184b92f52035d03d4f705600000000000000000000000000000000000000000000000022b1c8c1227a0000000000000000000000000000000000000000000000027b46536c66c8e3000000
-----Decoded View---------------
Arg [0] : asset_ (address): 0x52c64b8998eb7c80b6f526e99e29abdcc86b841b
Arg [1] : controller_ (address): 0xa59ef0208418559770a48d7ae4f260a28763167b
Arg [2] : long_ (address): 0x1cd33f4e6edeee8263aa07924c2760cf2ec8aad0
Arg [3] : short_ (address): 0x4243b34374cfb0a12f184b92f52035d03d4f7056
Arg [4] : targetLeverage_ (uint256): 2500000000000000000
Arg [5] : maxCollateral_ (uint256): 3000000000000000000000000
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 00000000000000000000000052c64b8998eb7c80b6f526e99e29abdcc86b841b
Arg [1] : 000000000000000000000000a59ef0208418559770a48d7ae4f260a28763167b
Arg [2] : 0000000000000000000000001cd33f4e6edeee8263aa07924c2760cf2ec8aad0
Arg [3] : 0000000000000000000000004243b34374cfb0a12f184b92f52035d03d4f7056
Arg [4] : 00000000000000000000000000000000000000000000000022b1c8c1227a0000
Arg [5] : 000000000000000000000000000000000000000000027b46536c66c8e3000000
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.