Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00
Cross-Chain Transactions
Loading...
Loading
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.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.gte(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/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.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 IPerennialVault {
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);
function deposit(UFixed18 assets, address account) external;
function redeem(UFixed18 shares, address account) external;
function claim(address account) external;
}
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,
VAULT_DEPOSIT,
VAULT_REDEEM,
VAULT_CLAIM,
VAULT_WRAP_AND_DEPOSIT
}
/// @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 self.oracleVersion != 0 && 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/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.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));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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.8.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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or 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 {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// 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
/// @solidity memory-safe-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.8.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @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 == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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);
}
}
}//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);
}{
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1000000
},
"remappings": [],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"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
6101606040523480156200001257600080fd5b50604051620045cc380380620045cc8339810160408190526200003591620000f5565b6001600160a01b0380871661014052851660808190526040805163d8dfeb4560e01b8152905163d8dfeb45916004808201926020929091908290030181865afa15801562000087573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ad919062000171565b6001600160a01b0390811660a05293841660c0529190921660e052610100919091526101205250620001989050565b6001600160a01b0381168114620000f257600080fd5b50565b60008060008060008060c087890312156200010f57600080fd5b86516200011c81620000dc565b60208801519096506200012f81620000dc565b60408801519095506200014281620000dc565b60608801519094506200015581620000dc565b809350506080870151915060a087015190509295509295509295565b6000602082840312156200018457600080fd5b81516200019181620000dc565b9392505050565b60805160a05160c05160e051610100516101205161014051614297620003356000396000818161030d015281816107130152818161087601528181610a5f01526116b20152600081816102b00152818161195201526119870152600081816103dc01526127d80152600081816102e601528181610da6015281816111630152818161130f0152818161161a01528181611f6e01528181612005015281816121840152818161247f015281816124e70152818161283301528181612a6e0152612be901526000818161021101528181610d17015281816110ae015281816112340152818161153b01528181611d9301528181611e2a015281816120e701528181612227015281816124a0015281816124c6015281816125110152818161257601528181612737015281816128090152818161298d0152612b35015260008181610403015281816108980152818161125f0152818161133d0152818161156a01528181611642015281816129b801528181612a96015281816130230152613107015260008181610468015281816123cf015261266201526142976000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c8063505bd3da116100f9578063c96f14b811610097578063d905777e11610071578063d905777e14610425578063dd62ed3e14610438578063f77c479114610463578063fff6cae91461048a57600080fd5b8063c96f14b8146103cf578063d6c946ea146103d7578063d8dfeb45146103fe57600080fd5b80637bde82f2116100d35780637bde82f21461038e57806395d89b41146103a1578063a9059cbb146103a9578063c6e6f592146103bc57600080fd5b8063505bd3da146103555780636e553f651461036857806370a082311461037b57600080fd5b806323b872dd1161016657806338bea4cb1161014057806338bea4cb146102e157806338d52e0f14610308578063402d267d1461032f5780634cd88b761461034257600080fd5b806323b872dd14610298578063251f47c1146102ab578063313ce567146102d257600080fd5b806307bfce37116101a257806307bfce371461020c578063095ea7b31461025857806318160ddd1461027b5780631e83409a1461028357600080fd5b806301e1d114146101c957806306fdde03146101e457806307a2d13a146101f9575b600080fd5b6101d1610492565b6040519081526020015b60405180910390f35b6101ec6104b1565b6040516101db9190613acf565b6101d1610207366004613b20565b61053f565b6102337f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101db565b61026b610266366004613b62565b61057c565b60405190151581526020016101db565b6101d16105f6565b610296610291366004613b8c565b61060f565b005b61026b6102a6366004613ba7565b610745565b6101d17f000000000000000000000000000000000000000000000000000000000000000081565b604051601281526020016101db565b6102337f000000000000000000000000000000000000000000000000000000000000000081565b6102337f000000000000000000000000000000000000000000000000000000000000000081565b6101d161033d366004613b8c565b610772565b610296610350366004613cbd565b61078b565b6101d1610363366004613b8c565b610919565b610296610376366004613d21565b610932565b6101d1610389366004613b8c565b610a96565b61029661039c366004613d21565b610aaf565b6101ec610c09565b61026b6103b7366004613b62565b610c16565b6101d16103ca366004613b20565b610c37565b6101d1610c6d565b6101d17f000000000000000000000000000000000000000000000000000000000000000081565b6102337f000000000000000000000000000000000000000000000000000000000000000081565b6101d1610433366004613b8c565b610c86565b6101d1610446366004613d4d565b600260209081526000928352604080842090915290825290205481565b6102337f000000000000000000000000000000000000000000000000000000000000000081565b610296610cab565b60008061049f6000610cc8565b5090506104ab81610f19565b91505090565b600080546104be90613d77565b80601f01602080910402602001604051908101604052809291908181526020018280546104ea90613d77565b80156105375780601f1061050c57610100808354040283529160200191610537565b820191906000526020600020905b81548152906001019060200180831161051a57829003601f168201915b505050505081565b60008061054c6000610cc8565b50905061055881610f19565b61056182610f7d565b604083015260208201526105758184610fac565b9392505050565b33600081815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906105e49086815260200190565b60405180910390a35060015b92915050565b6000806106036000610cc8565b5090506104ab81610f7d565b600061061a82610fdb565b5073ffffffffffffffffffffffffffffffffffffffff8316600090815260056020526040812080546006549290915591925061065681836114ec565b60065560405182815273ffffffffffffffffffffffffffffffffffffffff85169033907f70eb43c4a8ae8c40502dcf22436c509c28d6ff421cf07c491be56984bd9870689060200160405180910390a381600080806106b36114f8565b9194509250905060006106d0826106ca86866116f7565b906116f7565b90506106dc8187611703565b156106ef576106ec858288611717565b94505b6106f9888661172e565b61073a73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168a87611745565b505050505050505050565b600061075084610fdb565b505061075d843384611766565b610768848484611848565b5060019392505050565b60008061077f6000610cc8565b50905061057581611922565b6001806107b67f5db5abc19987c2b3729df7961b62b6bb0bae886dd47e3ce25bb3a3af34c6d80b5490565b106107f5576040517f1e7a9d95000000000000000000000000000000000000000000000000000000008152600481018290526024015b60405180910390fd5b61081e7f5db5abc19987c2b3729df7961b62b6bb0bae886dd47e3ce25bb3a3af34c6d80b829055565b60017fad57d7911b7e3d6c3c79a68ba909a7f4ba41f9485e5207b12dee0d0c6af5398c55600061084e8482613e10565b50600161085b8382613e10565b506108bc73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000006119ac565b60007fad57d7911b7e3d6c3c79a68ba909a7f4ba41f9485e5207b12dee0d0c6af5398c556040518181527fbe9b076dc5b65990cca9dd9d7366682482e7817a6f6bc7f4faf4dc32af497f32906020015b60405180910390a1505050565b60008061092583610cc8565b91505061057581846119ed565b600061093d82610fdb565b50905061095361094c82611922565b8490611aa7565b1561098a576040517f6726139b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60075461099790846116f7565b600755805160095573ffffffffffffffffffffffffffffffffffffffff82166000908152600a60205260409020546109cf90846116f7565b73ffffffffffffffffffffffffffffffffffffffff83166000818152600a60209081526040808320949094558451600c8252918490209190915583518351908152908101869052909133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610a8673ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163385611abd565b610a9181600061172e565b505050565b600080610aa283610cc8565b9150506105758184611adf565b3373ffffffffffffffffffffffffffffffffffffffff821614610ad757610ad7813384611766565b600080610ae383610fdb565b91509150610afc610af5838386611b99565b8590611aa7565b15610b33576040517f457c19da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854610b4090856116f7565b600855815160095573ffffffffffffffffffffffffffffffffffffffff83166000908152600b6020526040902054610b7890856116f7565b73ffffffffffffffffffffffffffffffffffffffff84166000818152600b60209081526040808320949094558551600c8252918490209190915584518351908152908101879052909133917fa28d80c9910787c0c058ed9b50c577f1389264bf61563fa45529e0771976f562910160405180910390a3610bf88385611bbb565b610c0382600061172e565b50505050565b600180546104be90613d77565b6000610c2133610fdb565b5050610c2e338484611848565b50600192915050565b600080610c446000610cc8565b509050610c5081610f19565b610c5982610f7d565b604083015260208201526105758184611c74565b600080610c7a6000610cc8565b5090506104ab81611ca3565b6000806000610c9484610cc8565b91509150610ca3828286611b99565b949350505050565b6000610cb76000610fdb565b509050610cc581600061172e565b50565b610cec60405180606001604052806000815260200160008152602001600081525090565b610d1060405180606001604052806000815260200160008152602001600081525090565b6000610e387f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610da49190613f2a565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e339190613f2a565b611cd2565b90506040518060600160405280828152602001610e56600954611ce8565b8152602001610e766009546000908152600d602052604090206002015490565b8152506040518060600160405280838152602001610ed2600c60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054611ce8565b815273ffffffffffffffffffffffffffffffffffffffff969096166000908152600c60209081526040808320548352600d8252909120600201549601959095529492505050565b600080600080610f276114f8565b91945092509050600080610f3f836106ca87876116f7565b610f4e6007546106ca8a611ca3565b9092509050610f5d8282611aa7565b610f68576000610f72565b610f7282826114ec565b979650505050505050565b6000600954826000015103610f9457505060045490565b6105f0610fa383600754611c74565b600454906116f7565b6000610fba83604001511590565b15610fc65750806105f0565b60208301516040840151610575918491611717565b610fff60405180606001604052806000815260200160008152602001600081525090565b61102360405180606001604052806000815260200160008152602001600081525090565b61102c83612071565b600954825192945090925010156114035761105a61105560045461104f85610f7d565b906114ec565b6122a8565b61106382611ca3565b6006556000600781905560085581516009556040805160c08101918290527fb7648fb9000000000000000000000000000000000000000000000000000000009091523060c4820152807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663b7648fb960e483016040805180830381865afa158015611108573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112c9190613f92565b5181526040517fb7648fb90000000000000000000000000000000000000000000000000000000081523060048201526020909101907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b7648fb9906024016040805180830381865afa1580156111be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e29190613f92565b518152600480546020830152604080517fcc218ece000000000000000000000000000000000000000000000000000000008152309281019290925273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660248401529201917f0000000000000000000000000000000000000000000000000000000000000000169063cc218ece90604401602060405180830381865afa1580156112a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ca9190613f2a565b81526040517fcc218ece00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660248301526020909201917f0000000000000000000000000000000000000000000000000000000000000000169063cc218ece90604401602060405180830381865afa158015611384573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a89190613f2a565b81526020016113b684610f19565b905282516000908152600d602090815260409182902083518155908301516001820155908201516002820155606082015160038201556080820151600482015560a0909101516005909101555b73ffffffffffffffffffffffffffffffffffffffff83161580159061144d575073ffffffffffffffffffffffffffffffffffffffff83166000908152600c60205260409020548151115b156114e75773ffffffffffffffffffffffffffffffffffffffff831660009081526003602052604090205461149190849061148c9061104f8584611adf565b6122bb565b61149b81846119ed565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260056020908152604080832093909355600a8152828220829055600b81528282208290558351600c909152919020555b915091565b60006105758284613fdd565b6040517fcc218ece00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166024830152600091829182917f00000000000000000000000000000000000000000000000000000000000000009091169063cc218ece90604401602060405180830381865afa1580156115b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d79190613f2a565b6040517fcc218ece00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063cc218ece90604401602060405180830381865afa158015611689573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116ad9190613f2a565b6116ec7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16612349565b925092509250909192565b60006105758284613ff0565b600061170f8383612355565b159392505050565b6000816117248486614003565b610ca39190614049565b6117378161238a565b61174182826125d7565b5050565b610a9173ffffffffffffffffffffffffffffffffffffffff84168383612860565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152600260209081526040808320938616835292905220546117c3907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612934565b156117cd57505050565b73ffffffffffffffffffffffffffffffffffffffff80841660009081526002602090815260408083209386168352929052205461180a90826114ec565b73ffffffffffffffffffffffffffffffffffffffff938416600090815260026020908152604080832095909616825293909352929091209190915550565b73ffffffffffffffffffffffffffffffffffffffff831660009081526003602052604090205461187890826114ec565b73ffffffffffffffffffffffffffffffffffffffff80851660009081526003602052604080822093909355908416815220546118b490826116f7565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526003602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906119159085815260200190565b60405180910390a3505050565b600061192d8261294a565b1561193a57506000919050565b600061194b6007546106ca85610f19565b90506119777f000000000000000000000000000000000000000000000000000000000000000082611aa7565b611982576000610575565b6105757f0000000000000000000000000000000000000000000000000000000000000000826114ec565b61174173ffffffffffffffffffffffffffffffffffffffff8316827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612c88565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600c6020526040812054835103611a46575073ffffffffffffffffffffffffffffffffffffffff81166000908152600560205260409020546105f0565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600b602052604090205461057590611a7b908590610fac565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260056020526040902054906116f7565b6000611ab38383612355565b6002149392505050565b610a9173ffffffffffffffffffffffffffffffffffffffff8416833084612e0a565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600c6020526040812054835103611b38575073ffffffffffffffffffffffffffffffffffffffff81166000908152600360205260409020546105f0565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600a602052604090205461057590611b6d908590611c74565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260036020526040902054906116f7565b6000611ba48461294a565b15611bb157506000610575565b610ca38383611adf565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260036020526040902054611beb90826114ec565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260036020526040902055600454611c1e90826114ec565b60045560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6000611c8283602001511590565b15611c8e5750806105f0565b60408301516020840151610575918491611717565b6000600954826000015103611cba57505060065490565b6105f0611cc983600854610fac565b600654906116f7565b6000818310611ce15781610575565b5090919050565b600080611ec5611d44611d23670de0b6b3a76400007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61405d565b6000868152600d6020526040902060030154611d3e90612e68565b90612ec7565b6000858152600d6020526040902054611ebf90611d6090612e68565b6040517f20fe9c3c00000000000000000000000000000000000000000000000000000000815260048101889052611d3e907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906320fe9c3c906024016040805180830381865afa158015611dee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e129190613f43565b5173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166320fe9c3c611e5a8b6001613ff0565b6040518263ffffffff1660e01b8152600401611e7891815260200190565b6040805180830381865afa158015611e94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb89190613f43565b5190612ee6565b90612ef2565b90506000612035611f1c611f01670de0b6b3a76400007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61405d565b6000878152600d6020526040902060040154611d3e90612e68565b6000868152600d6020526040902060010154611ebf90611f3b90612e68565b6040517f20fe9c3c00000000000000000000000000000000000000000000000000000000815260048101899052611d3e907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906320fe9c3c906024016040805180830381865afa158015611fc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fed9190613f43565b5173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166320fe9c3c611e5a8c6001613ff0565b9050610ca361206c6000611ebf8461206687612066600d60008d815260200190815260200160002060050154612e68565b90612efe565b612f0a565b61209560405180606001604052806000815260200160008152602001600081525090565b6120b960405180606001604052806000815260200160008152602001600081525090565b6040517ff667f8970000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063f667f89790602401600060405180830381600087803b15801561214057600080fd5b505af1158015612154573d6000803e3d6000fd5b50506040517ff667f8970000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16925063f667f8979150602401600060405180830381600087803b1580156121df57600080fd5b505af11580156121f3573d6000803e3d6000fd5b50506040517f8e480b20000000000000000000000000000000000000000000000000000000008152306004820152600092507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169150638e480b2090602401602060405180830381865afa158015612284573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e389190613f2a565b6004546122b590826116f7565b60045550565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600360205260409020546122eb90826116f7565b73ffffffffffffffffffffffffffffffffffffffff83166000818152600360205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90611c689085815260200190565b60006105f08230612f4a565b600082828082111561236c576002925050506105f0565b8082101561237f576000925050506105f0565b506001949350505050565b60008060006123976114f8565b9194509250905060006123b28561104f846106ca88886116f7565b905060006123c882671bc16d674ec80000612fde565b90506124637f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ba2de9bc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612438573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061245c9190613f2a565b8290611703565b1561246c575060005b6000806124798787611aa7565b6124c4577f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612507565b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000005b91509150612570827f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614612568578761256a565b885b85612ffd565b6125cd817f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614612568578761256a565b5050505050505050565b60006125e68261104f85610f19565b905060006126086126046008546004546116f790919063ffffffff16565b1590565b6126425761263d61263460045461262c6008546004546116f790919063ffffffff16565b859190611717565b600754906116f7565b61264f565b60075461264f90836116f7565b90506126f561245c671bc16d674ec800007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ba2de9bc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156126cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ef9190613f2a565b90613211565b156126fe575060005b83516040517f7ece075d00000000000000000000000000000000000000000000000000000000815260048101919091526000906127c0907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690637ece075d90602401606060405180830381865afa158015612793573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b791906140a9565b60400151613226565b90506000612802671bc16d674ec800006127fc8481877f0000000000000000000000000000000000000000000000000000000000000000613211565b90612fde565b905061282e7f000000000000000000000000000000000000000000000000000000000000000082613231565b6128587f000000000000000000000000000000000000000000000000000000000000000082613231565b505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052610a919084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613686565b60006129408383612355565b6001149392505050565b6040517f82df39de00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660248301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906382df39de90604401602060405180830381865afa158015612a01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a259190614105565b80612b0157506040517f82df39de00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660248301527f000000000000000000000000000000000000000000000000000000000000000016906382df39de90604401602060405180830381865afa158015612add573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b019190614105565b80612bb557506040517f58ca6f980000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906358ca6f9890602401602060405180830381865afa158015612b91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb59190614105565b80612c6957506040517f58ca6f980000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906358ca6f9890602401602060405180830381865afa158015612c45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c699190614105565b806105f057506040820151151580156105f057506020820151156105f0565b801580612d2857506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612d02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d269190613f2a565b155b612db4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e63650000000000000000000060648201526084016107ec565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052610a919084907f095ea7b300000000000000000000000000000000000000000000000000000000906064016128b2565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052610c039085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016128b2565b6000817f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8111156105f0576040517ea07eb5000000000000000000000000000000000000000000000000000000008152600481018290526024016107ec565b6000670de0b6b3a7640000612edc838561405d565b6105759190614127565b6000610575828461418f565b60006105758383613792565b600061057582846141b6565b600081818112156105f0576040517f501f289e000000000000000000000000000000000000000000000000000000008152600481018290526024016107ec565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152600091908416906370a0823190602401602060405180830381865afa158015612fba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105759190613f2a565b600081612ff3670de0b6b3a764000085614003565b6105759190614049565b6130078282611aa7565b156130e15773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663c3b35a7e308561305486866114ec565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff93841660048201529290911660248301526044820152606401600060405180830381600087803b1580156130c857600080fd5b505af11580156130dc573d6000803e3d6000fd5b505050505b6130eb8282611703565b156131c55773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663f213159c308561313885876114ec565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff93841660048201529290911660248301526044820152606401600060405180830381600087803b1580156131ac57600080fd5b505af11580156131c0573d6000803e3d6000fd5b505050505b6040805173ffffffffffffffffffffffffffffffffffffffff85168152602081018390527fdf43c689abc8d83e86b74634bbe0ba590e2ecb34f3f432a612c6edef3e532cbd910161090c565b6000670de0b6b3a7640000612ff38385614003565b60006105f0826137a1565b6040517f1e0c6fb900000000000000000000000000000000000000000000000000000000815230600482015260009061335a9073ffffffffffffffffffffffffffffffffffffffff851690631e0c6fb99060240160a060405180830381865afa1580156132a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132c691906141de565b6040517fb7648fb900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff86169063b7648fb9906024015b6040805180830381865afa158015613330573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133549190613f92565b906137b8565b600001519050600061347d8473ffffffffffffffffffffffffffffffffffffffff166359ea287d6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156133b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133d491906141de565b8573ffffffffffffffffffffffffffffffffffffffff16639a427d038773ffffffffffffffffffffffffffffffffffffffff1663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa15801561343b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061345f9190613f2a565b6040518263ffffffff1660e01b815260040161331491815260200190565b60000151905060008473ffffffffffffffffffffffffffffffffffffffff1663193775676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156134d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134f49190613f2a565b905060006135028284611aa7565b61350d576000613517565b61351782846114ec565b90506135238585611703565b1561359f5773ffffffffffffffffffffffffffffffffffffffff86166359218fe961354e86886114ec565b6040518263ffffffff1660e01b815260040161356c91815260200190565b600060405180830381600087803b15801561358657600080fd5b505af115801561359a573d6000803e3d6000fd5b505050505b6135a985856137e7565b1561362f5773ffffffffffffffffffffffffffffffffffffffff861663d7d7d6b86135de836135d889896114ec565b90613803565b6040518263ffffffff1660e01b81526004016135fc91815260200190565b600060405180830381600087803b15801561361657600080fd5b505af115801561362a573d6000803e3d6000fd5b505050505b6040805173ffffffffffffffffffffffffffffffffffffffff88168152602081018790527f814d2340073b1f05a68bb3f6aab82bfc4eea78f35f7c2332caddf0c35865331e910160405180910390a1505050505050565b60006136e8826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff1661380f9092919063ffffffff16565b805190915015610a9157808060200190518101906137069190614105565b610a91576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016107ec565b6000818313611ce15781610575565b6000808212156137b457816000036105f0565b5090565b60408051808201909152600080825260208201526105756137dd84846020015161381e565b8360400151613875565b60006137f38383611aa7565b8061057557506105758383612934565b60006105758383611cd2565b6060610ca384846000856138c3565b60408051808201909152600080825260208201526040805180820190915282518451829161384c91906116f7565b815260200161386c846020015186602001516116f790919063ffffffff16565b90529392505050565b6040805180820190915260008082526020820152604080518082019091528251845182916138a391906114ec565b815260200161386c846020015186602001516114ec90919063ffffffff16565b606082471015613955576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016107ec565b6000808673ffffffffffffffffffffffffffffffffffffffff16858760405161397e9190614245565b60006040518083038185875af1925050503d80600081146139bb576040519150601f19603f3d011682016040523d82523d6000602084013e6139c0565b606091505b5091509150610f728783838760608315613a62578251600003613a5b5773ffffffffffffffffffffffffffffffffffffffff85163b613a5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107ec565b5081610ca3565b610ca38383815115613a775781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107ec9190613acf565b60005b83811015613ac6578181015183820152602001613aae565b50506000910152565b6020815260008251806020840152613aee816040850160208701613aab565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b600060208284031215613b3257600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114613b5d57600080fd5b919050565b60008060408385031215613b7557600080fd5b613b7e83613b39565b946020939093013593505050565b600060208284031215613b9e57600080fd5b61057582613b39565b600080600060608486031215613bbc57600080fd5b613bc584613b39565b9250613bd360208501613b39565b9150604084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112613c2357600080fd5b813567ffffffffffffffff80821115613c3e57613c3e613be3565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715613c8457613c84613be3565b81604052838152866020858801011115613c9d57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060408385031215613cd057600080fd5b823567ffffffffffffffff80821115613ce857600080fd5b613cf486838701613c12565b93506020850135915080821115613d0a57600080fd5b50613d1785828601613c12565b9150509250929050565b60008060408385031215613d3457600080fd5b82359150613d4460208401613b39565b90509250929050565b60008060408385031215613d6057600080fd5b613d6983613b39565b9150613d4460208401613b39565b600181811c90821680613d8b57607f821691505b602082108103613dc4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f821115610a9157600081815260208120601f850160051c81016020861015613df15750805b601f850160051c820191505b8181101561285857828155600101613dfd565b815167ffffffffffffffff811115613e2a57613e2a613be3565b613e3e81613e388454613d77565b84613dca565b602080601f831160018114613e915760008415613e5b5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555612858565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613ede57888601518255948401946001909101908401613ebf565b5085821015613f1a57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215613f3c57600080fd5b5051919050565b600060408284031215613f5557600080fd5b6040516040810181811067ffffffffffffffff82111715613f7857613f78613be3565b604052825181526020928301519281019290925250919050565b600060408284031215613fa457600080fd5b6105758383613f43565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156105f0576105f0613fae565b808201808211156105f0576105f0613fae565b80820281158282048414176105f0576105f0613fae565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826140585761405861401a565b500490565b808202600082127f80000000000000000000000000000000000000000000000000000000000000008414161561409557614095613fae565b81810583148215176105f0576105f0613fae565b6000606082840312156140bb57600080fd5b6040516060810181811067ffffffffffffffff821117156140de576140de613be3565b80604052508251815260208301516020820152604083015160408201528091505092915050565b60006020828403121561411757600080fd5b8151801515811461057557600080fd5b6000826141365761413661401a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561418a5761418a613fae565b500590565b81810360008312801583831316838312821617156141af576141af613fae565b5092915050565b80820182811260008312801582168215821617156141d6576141d6613fae565b505092915050565b600060a082840312156141f057600080fd5b6040516060810181811067ffffffffffffffff8211171561421357614213613be3565b604052825181526142278460208501613f43565b60208201526142398460608501613f43565b60408201529392505050565b60008251614257818460208701613aab565b919091019291505056fea26469706673582212209d02b13945e8d251b5265a6bd1a9b0021eb5f0b60d146b2025906d0f7c29f68164736f6c6343000811003300000000000000000000000052c64b8998eb7c80b6f526e99e29abdcc86b841b000000000000000000000000a59ef0208418559770a48d7ae4f260a28763167b000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e00000000000000000000000000000000000000000000000014d1120d7b16000000000000000000000000000000000000000000000004f68ca6d8cd91c6000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101c45760003560e01c8063505bd3da116100f9578063c96f14b811610097578063d905777e11610071578063d905777e14610425578063dd62ed3e14610438578063f77c479114610463578063fff6cae91461048a57600080fd5b8063c96f14b8146103cf578063d6c946ea146103d7578063d8dfeb45146103fe57600080fd5b80637bde82f2116100d35780637bde82f21461038e57806395d89b41146103a1578063a9059cbb146103a9578063c6e6f592146103bc57600080fd5b8063505bd3da146103555780636e553f651461036857806370a082311461037b57600080fd5b806323b872dd1161016657806338bea4cb1161014057806338bea4cb146102e157806338d52e0f14610308578063402d267d1461032f5780634cd88b761461034257600080fd5b806323b872dd14610298578063251f47c1146102ab578063313ce567146102d257600080fd5b806307bfce37116101a257806307bfce371461020c578063095ea7b31461025857806318160ddd1461027b5780631e83409a1461028357600080fd5b806301e1d114146101c957806306fdde03146101e457806307a2d13a146101f9575b600080fd5b6101d1610492565b6040519081526020015b60405180910390f35b6101ec6104b1565b6040516101db9190613acf565b6101d1610207366004613b20565b61053f565b6102337f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd81565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101db565b61026b610266366004613b62565b61057c565b60405190151581526020016101db565b6101d16105f6565b610296610291366004613b8c565b61060f565b005b61026b6102a6366004613ba7565b610745565b6101d17f00000000000000000000000000000000000000000004f68ca6d8cd91c600000081565b604051601281526020016101db565b6102337f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e81565b6102337f00000000000000000000000052c64b8998eb7c80b6f526e99e29abdcc86b841b81565b6101d161033d366004613b8c565b610772565b610296610350366004613cbd565b61078b565b6101d1610363366004613b8c565b610919565b610296610376366004613d21565b610932565b6101d1610389366004613b8c565b610a96565b61029661039c366004613d21565b610aaf565b6101ec610c09565b61026b6103b7366004613b62565b610c16565b6101d16103ca366004613b20565b610c37565b6101d1610c6d565b6101d17f00000000000000000000000000000000000000000000000014d1120d7b16000081565b6102337f000000000000000000000000af8ced28fce00abd30463d55da81156aa5aeeec281565b6101d1610433366004613b8c565b610c86565b6101d1610446366004613d4d565b600260209081526000928352604080842090915290825290205481565b6102337f000000000000000000000000a59ef0208418559770a48d7ae4f260a28763167b81565b610296610cab565b60008061049f6000610cc8565b5090506104ab81610f19565b91505090565b600080546104be90613d77565b80601f01602080910402602001604051908101604052809291908181526020018280546104ea90613d77565b80156105375780601f1061050c57610100808354040283529160200191610537565b820191906000526020600020905b81548152906001019060200180831161051a57829003601f168201915b505050505081565b60008061054c6000610cc8565b50905061055881610f19565b61056182610f7d565b604083015260208201526105758184610fac565b9392505050565b33600081815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906105e49086815260200190565b60405180910390a35060015b92915050565b6000806106036000610cc8565b5090506104ab81610f7d565b600061061a82610fdb565b5073ffffffffffffffffffffffffffffffffffffffff8316600090815260056020526040812080546006549290915591925061065681836114ec565b60065560405182815273ffffffffffffffffffffffffffffffffffffffff85169033907f70eb43c4a8ae8c40502dcf22436c509c28d6ff421cf07c491be56984bd9870689060200160405180910390a381600080806106b36114f8565b9194509250905060006106d0826106ca86866116f7565b906116f7565b90506106dc8187611703565b156106ef576106ec858288611717565b94505b6106f9888661172e565b61073a73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052c64b8998eb7c80b6f526e99e29abdcc86b841b168a87611745565b505050505050505050565b600061075084610fdb565b505061075d843384611766565b610768848484611848565b5060019392505050565b60008061077f6000610cc8565b50905061057581611922565b6001806107b67f5db5abc19987c2b3729df7961b62b6bb0bae886dd47e3ce25bb3a3af34c6d80b5490565b106107f5576040517f1e7a9d95000000000000000000000000000000000000000000000000000000008152600481018290526024015b60405180910390fd5b61081e7f5db5abc19987c2b3729df7961b62b6bb0bae886dd47e3ce25bb3a3af34c6d80b829055565b60017fad57d7911b7e3d6c3c79a68ba909a7f4ba41f9485e5207b12dee0d0c6af5398c55600061084e8482613e10565b50600161085b8382613e10565b506108bc73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052c64b8998eb7c80b6f526e99e29abdcc86b841b167f000000000000000000000000af8ced28fce00abd30463d55da81156aa5aeeec26119ac565b60007fad57d7911b7e3d6c3c79a68ba909a7f4ba41f9485e5207b12dee0d0c6af5398c556040518181527fbe9b076dc5b65990cca9dd9d7366682482e7817a6f6bc7f4faf4dc32af497f32906020015b60405180910390a1505050565b60008061092583610cc8565b91505061057581846119ed565b600061093d82610fdb565b50905061095361094c82611922565b8490611aa7565b1561098a576040517f6726139b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60075461099790846116f7565b600755805160095573ffffffffffffffffffffffffffffffffffffffff82166000908152600a60205260409020546109cf90846116f7565b73ffffffffffffffffffffffffffffffffffffffff83166000818152600a60209081526040808320949094558451600c8252918490209190915583518351908152908101869052909133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610a8673ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052c64b8998eb7c80b6f526e99e29abdcc86b841b163385611abd565b610a9181600061172e565b505050565b600080610aa283610cc8565b9150506105758184611adf565b3373ffffffffffffffffffffffffffffffffffffffff821614610ad757610ad7813384611766565b600080610ae383610fdb565b91509150610afc610af5838386611b99565b8590611aa7565b15610b33576040517f457c19da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854610b4090856116f7565b600855815160095573ffffffffffffffffffffffffffffffffffffffff83166000908152600b6020526040902054610b7890856116f7565b73ffffffffffffffffffffffffffffffffffffffff84166000818152600b60209081526040808320949094558551600c8252918490209190915584518351908152908101879052909133917fa28d80c9910787c0c058ed9b50c577f1389264bf61563fa45529e0771976f562910160405180910390a3610bf88385611bbb565b610c0382600061172e565b50505050565b600180546104be90613d77565b6000610c2133610fdb565b5050610c2e338484611848565b50600192915050565b600080610c446000610cc8565b509050610c5081610f19565b610c5982610f7d565b604083015260208201526105758184611c74565b600080610c7a6000610cc8565b5090506104ab81611ca3565b6000806000610c9484610cc8565b91509150610ca3828286611b99565b949350505050565b6000610cb76000610fdb565b509050610cc581600061172e565b50565b610cec60405180606001604052806000815260200160008152602001600081525090565b610d1060405180606001604052806000815260200160008152602001600081525090565b6000610e387f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd73ffffffffffffffffffffffffffffffffffffffff1663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610da49190613f2a565b7f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e73ffffffffffffffffffffffffffffffffffffffff1663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e339190613f2a565b611cd2565b90506040518060600160405280828152602001610e56600954611ce8565b8152602001610e766009546000908152600d602052604090206002015490565b8152506040518060600160405280838152602001610ed2600c60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054611ce8565b815273ffffffffffffffffffffffffffffffffffffffff969096166000908152600c60209081526040808320548352600d8252909120600201549601959095529492505050565b600080600080610f276114f8565b91945092509050600080610f3f836106ca87876116f7565b610f4e6007546106ca8a611ca3565b9092509050610f5d8282611aa7565b610f68576000610f72565b610f7282826114ec565b979650505050505050565b6000600954826000015103610f9457505060045490565b6105f0610fa383600754611c74565b600454906116f7565b6000610fba83604001511590565b15610fc65750806105f0565b60208301516040840151610575918491611717565b610fff60405180606001604052806000815260200160008152602001600081525090565b61102360405180606001604052806000815260200160008152602001600081525090565b61102c83612071565b600954825192945090925010156114035761105a61105560045461104f85610f7d565b906114ec565b6122a8565b61106382611ca3565b6006556000600781905560085581516009556040805160c08101918290527fb7648fb9000000000000000000000000000000000000000000000000000000009091523060c4820152807f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd73ffffffffffffffffffffffffffffffffffffffff1663b7648fb960e483016040805180830381865afa158015611108573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112c9190613f92565b5181526040517fb7648fb90000000000000000000000000000000000000000000000000000000081523060048201526020909101907f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e73ffffffffffffffffffffffffffffffffffffffff169063b7648fb9906024016040805180830381865afa1580156111be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e29190613f92565b518152600480546020830152604080517fcc218ece000000000000000000000000000000000000000000000000000000008152309281019290925273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd811660248401529201917f000000000000000000000000af8ced28fce00abd30463d55da81156aa5aeeec2169063cc218ece90604401602060405180830381865afa1580156112a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ca9190613f2a565b81526040517fcc218ece00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e811660248301526020909201917f000000000000000000000000af8ced28fce00abd30463d55da81156aa5aeeec2169063cc218ece90604401602060405180830381865afa158015611384573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a89190613f2a565b81526020016113b684610f19565b905282516000908152600d602090815260409182902083518155908301516001820155908201516002820155606082015160038201556080820151600482015560a0909101516005909101555b73ffffffffffffffffffffffffffffffffffffffff83161580159061144d575073ffffffffffffffffffffffffffffffffffffffff83166000908152600c60205260409020548151115b156114e75773ffffffffffffffffffffffffffffffffffffffff831660009081526003602052604090205461149190849061148c9061104f8584611adf565b6122bb565b61149b81846119ed565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260056020908152604080832093909355600a8152828220829055600b81528282208290558351600c909152919020555b915091565b60006105758284613fdd565b6040517fcc218ece00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd81166024830152600091829182917f000000000000000000000000af8ced28fce00abd30463d55da81156aa5aeeec29091169063cc218ece90604401602060405180830381865afa1580156115b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d79190613f2a565b6040517fcc218ece00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e811660248301527f000000000000000000000000af8ced28fce00abd30463d55da81156aa5aeeec2169063cc218ece90604401602060405180830381865afa158015611689573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116ad9190613f2a565b6116ec7f00000000000000000000000052c64b8998eb7c80b6f526e99e29abdcc86b841b73ffffffffffffffffffffffffffffffffffffffff16612349565b925092509250909192565b60006105758284613ff0565b600061170f8383612355565b159392505050565b6000816117248486614003565b610ca39190614049565b6117378161238a565b61174182826125d7565b5050565b610a9173ffffffffffffffffffffffffffffffffffffffff84168383612860565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152600260209081526040808320938616835292905220546117c3907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612934565b156117cd57505050565b73ffffffffffffffffffffffffffffffffffffffff80841660009081526002602090815260408083209386168352929052205461180a90826114ec565b73ffffffffffffffffffffffffffffffffffffffff938416600090815260026020908152604080832095909616825293909352929091209190915550565b73ffffffffffffffffffffffffffffffffffffffff831660009081526003602052604090205461187890826114ec565b73ffffffffffffffffffffffffffffffffffffffff80851660009081526003602052604080822093909355908416815220546118b490826116f7565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526003602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906119159085815260200190565b60405180910390a3505050565b600061192d8261294a565b1561193a57506000919050565b600061194b6007546106ca85610f19565b90506119777f00000000000000000000000000000000000000000004f68ca6d8cd91c600000082611aa7565b611982576000610575565b6105757f00000000000000000000000000000000000000000004f68ca6d8cd91c6000000826114ec565b61174173ffffffffffffffffffffffffffffffffffffffff8316827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612c88565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600c6020526040812054835103611a46575073ffffffffffffffffffffffffffffffffffffffff81166000908152600560205260409020546105f0565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600b602052604090205461057590611a7b908590610fac565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260056020526040902054906116f7565b6000611ab38383612355565b6002149392505050565b610a9173ffffffffffffffffffffffffffffffffffffffff8416833084612e0a565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600c6020526040812054835103611b38575073ffffffffffffffffffffffffffffffffffffffff81166000908152600360205260409020546105f0565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600a602052604090205461057590611b6d908590611c74565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260036020526040902054906116f7565b6000611ba48461294a565b15611bb157506000610575565b610ca38383611adf565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260036020526040902054611beb90826114ec565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260036020526040902055600454611c1e90826114ec565b60045560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6000611c8283602001511590565b15611c8e5750806105f0565b60408301516020840151610575918491611717565b6000600954826000015103611cba57505060065490565b6105f0611cc983600854610fac565b600654906116f7565b6000818310611ce15781610575565b5090919050565b600080611ec5611d44611d23670de0b6b3a76400007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61405d565b6000868152600d6020526040902060030154611d3e90612e68565b90612ec7565b6000858152600d6020526040902054611ebf90611d6090612e68565b6040517f20fe9c3c00000000000000000000000000000000000000000000000000000000815260048101889052611d3e907f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd73ffffffffffffffffffffffffffffffffffffffff16906320fe9c3c906024016040805180830381865afa158015611dee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e129190613f43565b5173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd166320fe9c3c611e5a8b6001613ff0565b6040518263ffffffff1660e01b8152600401611e7891815260200190565b6040805180830381865afa158015611e94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb89190613f43565b5190612ee6565b90612ef2565b90506000612035611f1c611f01670de0b6b3a76400007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61405d565b6000878152600d6020526040902060040154611d3e90612e68565b6000868152600d6020526040902060010154611ebf90611f3b90612e68565b6040517f20fe9c3c00000000000000000000000000000000000000000000000000000000815260048101899052611d3e907f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e73ffffffffffffffffffffffffffffffffffffffff16906320fe9c3c906024016040805180830381865afa158015611fc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fed9190613f43565b5173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e166320fe9c3c611e5a8c6001613ff0565b9050610ca361206c6000611ebf8461206687612066600d60008d815260200190815260200160002060050154612e68565b90612efe565b612f0a565b61209560405180606001604052806000815260200160008152602001600081525090565b6120b960405180606001604052806000815260200160008152602001600081525090565b6040517ff667f8970000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd73ffffffffffffffffffffffffffffffffffffffff169063f667f89790602401600060405180830381600087803b15801561214057600080fd5b505af1158015612154573d6000803e3d6000fd5b50506040517ff667f8970000000000000000000000000000000000000000000000000000000081523060048201527f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e73ffffffffffffffffffffffffffffffffffffffff16925063f667f8979150602401600060405180830381600087803b1580156121df57600080fd5b505af11580156121f3573d6000803e3d6000fd5b50506040517f8e480b20000000000000000000000000000000000000000000000000000000008152306004820152600092507f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd73ffffffffffffffffffffffffffffffffffffffff169150638e480b2090602401602060405180830381865afa158015612284573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e389190613f2a565b6004546122b590826116f7565b60045550565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600360205260409020546122eb90826116f7565b73ffffffffffffffffffffffffffffffffffffffff83166000818152600360205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90611c689085815260200190565b60006105f08230612f4a565b600082828082111561236c576002925050506105f0565b8082101561237f576000925050506105f0565b506001949350505050565b60008060006123976114f8565b9194509250905060006123b28561104f846106ca88886116f7565b905060006123c882671bc16d674ec80000612fde565b90506124637f000000000000000000000000a59ef0208418559770a48d7ae4f260a28763167b73ffffffffffffffffffffffffffffffffffffffff1663ba2de9bc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612438573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061245c9190613f2a565b8290611703565b1561246c575060005b6000806124798787611aa7565b6124c4577f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e7f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd612507565b7f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd7f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e5b91509150612570827f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614612568578761256a565b885b85612ffd565b6125cd817f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614612568578761256a565b5050505050505050565b60006125e68261104f85610f19565b905060006126086126046008546004546116f790919063ffffffff16565b1590565b6126425761263d61263460045461262c6008546004546116f790919063ffffffff16565b859190611717565b600754906116f7565b61264f565b60075461264f90836116f7565b90506126f561245c671bc16d674ec800007f000000000000000000000000a59ef0208418559770a48d7ae4f260a28763167b73ffffffffffffffffffffffffffffffffffffffff1663ba2de9bc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156126cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ef9190613f2a565b90613211565b156126fe575060005b83516040517f7ece075d00000000000000000000000000000000000000000000000000000000815260048101919091526000906127c0907f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd73ffffffffffffffffffffffffffffffffffffffff1690637ece075d90602401606060405180830381865afa158015612793573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b791906140a9565b60400151613226565b90506000612802671bc16d674ec800006127fc8481877f00000000000000000000000000000000000000000000000014d1120d7b160000613211565b90612fde565b905061282e7f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd82613231565b6128587f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e82613231565b505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052610a919084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613686565b60006129408383612355565b6001149392505050565b6040517f82df39de00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd811660248301526000917f000000000000000000000000af8ced28fce00abd30463d55da81156aa5aeeec2909116906382df39de90604401602060405180830381865afa158015612a01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a259190614105565b80612b0157506040517f82df39de00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e811660248301527f000000000000000000000000af8ced28fce00abd30463d55da81156aa5aeeec216906382df39de90604401602060405180830381865afa158015612add573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b019190614105565b80612bb557506040517f58ca6f980000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd73ffffffffffffffffffffffffffffffffffffffff16906358ca6f9890602401602060405180830381865afa158015612b91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb59190614105565b80612c6957506040517f58ca6f980000000000000000000000000000000000000000000000000000000081523060048201527f0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e73ffffffffffffffffffffffffffffffffffffffff16906358ca6f9890602401602060405180830381865afa158015612c45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c699190614105565b806105f057506040820151151580156105f057506020820151156105f0565b801580612d2857506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612d02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d269190613f2a565b155b612db4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e63650000000000000000000060648201526084016107ec565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052610a919084907f095ea7b300000000000000000000000000000000000000000000000000000000906064016128b2565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052610c039085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016128b2565b6000817f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8111156105f0576040517ea07eb5000000000000000000000000000000000000000000000000000000008152600481018290526024016107ec565b6000670de0b6b3a7640000612edc838561405d565b6105759190614127565b6000610575828461418f565b60006105758383613792565b600061057582846141b6565b600081818112156105f0576040517f501f289e000000000000000000000000000000000000000000000000000000008152600481018290526024016107ec565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152600091908416906370a0823190602401602060405180830381865afa158015612fba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105759190613f2a565b600081612ff3670de0b6b3a764000085614003565b6105759190614049565b6130078282611aa7565b156130e15773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000af8ced28fce00abd30463d55da81156aa5aeeec21663c3b35a7e308561305486866114ec565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff93841660048201529290911660248301526044820152606401600060405180830381600087803b1580156130c857600080fd5b505af11580156130dc573d6000803e3d6000fd5b505050505b6130eb8282611703565b156131c55773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000af8ced28fce00abd30463d55da81156aa5aeeec21663f213159c308561313885876114ec565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff93841660048201529290911660248301526044820152606401600060405180830381600087803b1580156131ac57600080fd5b505af11580156131c0573d6000803e3d6000fd5b505050505b6040805173ffffffffffffffffffffffffffffffffffffffff85168152602081018390527fdf43c689abc8d83e86b74634bbe0ba590e2ecb34f3f432a612c6edef3e532cbd910161090c565b6000670de0b6b3a7640000612ff38385614003565b60006105f0826137a1565b6040517f1e0c6fb900000000000000000000000000000000000000000000000000000000815230600482015260009061335a9073ffffffffffffffffffffffffffffffffffffffff851690631e0c6fb99060240160a060405180830381865afa1580156132a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132c691906141de565b6040517fb7648fb900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff86169063b7648fb9906024015b6040805180830381865afa158015613330573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133549190613f92565b906137b8565b600001519050600061347d8473ffffffffffffffffffffffffffffffffffffffff166359ea287d6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156133b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133d491906141de565b8573ffffffffffffffffffffffffffffffffffffffff16639a427d038773ffffffffffffffffffffffffffffffffffffffff1663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa15801561343b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061345f9190613f2a565b6040518263ffffffff1660e01b815260040161331491815260200190565b60000151905060008473ffffffffffffffffffffffffffffffffffffffff1663193775676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156134d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134f49190613f2a565b905060006135028284611aa7565b61350d576000613517565b61351782846114ec565b90506135238585611703565b1561359f5773ffffffffffffffffffffffffffffffffffffffff86166359218fe961354e86886114ec565b6040518263ffffffff1660e01b815260040161356c91815260200190565b600060405180830381600087803b15801561358657600080fd5b505af115801561359a573d6000803e3d6000fd5b505050505b6135a985856137e7565b1561362f5773ffffffffffffffffffffffffffffffffffffffff861663d7d7d6b86135de836135d889896114ec565b90613803565b6040518263ffffffff1660e01b81526004016135fc91815260200190565b600060405180830381600087803b15801561361657600080fd5b505af115801561362a573d6000803e3d6000fd5b505050505b6040805173ffffffffffffffffffffffffffffffffffffffff88168152602081018790527f814d2340073b1f05a68bb3f6aab82bfc4eea78f35f7c2332caddf0c35865331e910160405180910390a1505050505050565b60006136e8826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff1661380f9092919063ffffffff16565b805190915015610a9157808060200190518101906137069190614105565b610a91576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016107ec565b6000818313611ce15781610575565b6000808212156137b457816000036105f0565b5090565b60408051808201909152600080825260208201526105756137dd84846020015161381e565b8360400151613875565b60006137f38383611aa7565b8061057557506105758383612934565b60006105758383611cd2565b6060610ca384846000856138c3565b60408051808201909152600080825260208201526040805180820190915282518451829161384c91906116f7565b815260200161386c846020015186602001516116f790919063ffffffff16565b90529392505050565b6040805180820190915260008082526020820152604080518082019091528251845182916138a391906114ec565b815260200161386c846020015186602001516114ec90919063ffffffff16565b606082471015613955576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016107ec565b6000808673ffffffffffffffffffffffffffffffffffffffff16858760405161397e9190614245565b60006040518083038185875af1925050503d80600081146139bb576040519150601f19603f3d011682016040523d82523d6000602084013e6139c0565b606091505b5091509150610f728783838760608315613a62578251600003613a5b5773ffffffffffffffffffffffffffffffffffffffff85163b613a5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107ec565b5081610ca3565b610ca38383815115613a775781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107ec9190613acf565b60005b83811015613ac6578181015183820152602001613aae565b50506000910152565b6020815260008251806020840152613aee816040850160208701613aab565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b600060208284031215613b3257600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114613b5d57600080fd5b919050565b60008060408385031215613b7557600080fd5b613b7e83613b39565b946020939093013593505050565b600060208284031215613b9e57600080fd5b61057582613b39565b600080600060608486031215613bbc57600080fd5b613bc584613b39565b9250613bd360208501613b39565b9150604084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112613c2357600080fd5b813567ffffffffffffffff80821115613c3e57613c3e613be3565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715613c8457613c84613be3565b81604052838152866020858801011115613c9d57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060408385031215613cd057600080fd5b823567ffffffffffffffff80821115613ce857600080fd5b613cf486838701613c12565b93506020850135915080821115613d0a57600080fd5b50613d1785828601613c12565b9150509250929050565b60008060408385031215613d3457600080fd5b82359150613d4460208401613b39565b90509250929050565b60008060408385031215613d6057600080fd5b613d6983613b39565b9150613d4460208401613b39565b600181811c90821680613d8b57607f821691505b602082108103613dc4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f821115610a9157600081815260208120601f850160051c81016020861015613df15750805b601f850160051c820191505b8181101561285857828155600101613dfd565b815167ffffffffffffffff811115613e2a57613e2a613be3565b613e3e81613e388454613d77565b84613dca565b602080601f831160018114613e915760008415613e5b5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555612858565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613ede57888601518255948401946001909101908401613ebf565b5085821015613f1a57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215613f3c57600080fd5b5051919050565b600060408284031215613f5557600080fd5b6040516040810181811067ffffffffffffffff82111715613f7857613f78613be3565b604052825181526020928301519281019290925250919050565b600060408284031215613fa457600080fd5b6105758383613f43565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156105f0576105f0613fae565b808201808211156105f0576105f0613fae565b80820281158282048414176105f0576105f0613fae565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826140585761405861401a565b500490565b808202600082127f80000000000000000000000000000000000000000000000000000000000000008414161561409557614095613fae565b81810583148215176105f0576105f0613fae565b6000606082840312156140bb57600080fd5b6040516060810181811067ffffffffffffffff821117156140de576140de613be3565b80604052508251815260208301516020820152604083015160408201528091505092915050565b60006020828403121561411757600080fd5b8151801515811461057557600080fd5b6000826141365761413661401a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561418a5761418a613fae565b500590565b81810360008312801583831316838312821617156141af576141af613fae565b5092915050565b80820182811260008312801582168215821617156141d6576141d6613fae565b505092915050565b600060a082840312156141f057600080fd5b6040516060810181811067ffffffffffffffff8211171561421357614213613be3565b604052825181526142278460208501613f43565b60208201526142398460608501613f43565b60408201529392505050565b60008251614257818460208701613aab565b919091019291505056fea26469706673582212209d02b13945e8d251b5265a6bd1a9b0021eb5f0b60d146b2025906d0f7c29f68164736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000052c64b8998eb7c80b6f526e99e29abdcc86b841b000000000000000000000000a59ef0208418559770a48d7ae4f260a28763167b000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e00000000000000000000000000000000000000000000000014d1120d7b16000000000000000000000000000000000000000000000004f68ca6d8cd91c6000000
-----Decoded View---------------
Arg [0] : asset_ (address): 0x52C64b8998eB7C80b6F526E99E29ABdcC86B841b
Arg [1] : controller_ (address): 0xA59eF0208418559770a48D7ae4f260A28763167B
Arg [2] : long_ (address): 0x260422C091Da8C88ef21f5D1112AB43aa94787cd
Arg [3] : short_ (address): 0x5b99d122aF97bA012aA237Bd01577278BfAaff1e
Arg [4] : targetLeverage_ (uint256): 1500000000000000000
Arg [5] : maxCollateral_ (uint256): 6000000000000000000000000
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 00000000000000000000000052c64b8998eb7c80b6f526e99e29abdcc86b841b
Arg [1] : 000000000000000000000000a59ef0208418559770a48d7ae4f260a28763167b
Arg [2] : 000000000000000000000000260422c091da8c88ef21f5d1112ab43aa94787cd
Arg [3] : 0000000000000000000000005b99d122af97ba012aa237bd01577278bfaaff1e
Arg [4] : 00000000000000000000000000000000000000000000000014d1120d7b160000
Arg [5] : 00000000000000000000000000000000000000000004f68ca6d8cd91c6000000
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.