Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
PerformanceFee
Compiler Version
v0.6.12+commit.27d51765
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/math/SignedSafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "../../../core/fund/comptroller/ComptrollerLib.sol"; import "../FeeManager.sol"; import "./utils/FeeBase.sol"; /// @title PerformanceFee Contract /// @author Enzyme Council <[email protected]> /// @notice A performance-based fee with configurable rate and crystallization period, using /// a high watermark /// @dev This contract assumes that all shares in the VaultProxy are shares outstanding, /// which is fine for this release. Even if they are not, they are still shares that /// are only claimable by the fund owner. contract PerformanceFee is FeeBase { using SafeMath for uint256; using SignedSafeMath for int256; event ActivatedForFund(address indexed comptrollerProxy, uint256 highWaterMark); event FundSettingsAdded(address indexed comptrollerProxy, uint256 rate, uint256 period); event LastSharePriceUpdated( address indexed comptrollerProxy, uint256 prevSharePrice, uint256 nextSharePrice ); event PaidOut( address indexed comptrollerProxy, uint256 prevHighWaterMark, uint256 nextHighWaterMark, uint256 aggregateValueDue ); event PerformanceUpdated( address indexed comptrollerProxy, uint256 prevAggregateValueDue, uint256 nextAggregateValueDue, int256 sharesOutstandingDiff ); struct FeeInfo { uint256 rate; uint256 period; uint256 activated; uint256 lastPaid; uint256 highWaterMark; uint256 lastSharePrice; uint256 aggregateValueDue; } uint256 private constant RATE_DIVISOR = 10**18; uint256 private constant SHARE_UNIT = 10**18; mapping(address => FeeInfo) private comptrollerProxyToFeeInfo; constructor(address _feeManager) public FeeBase(_feeManager) {} // EXTERNAL FUNCTIONS /// @notice Activates the fee for a fund /// @param _comptrollerProxy The ComptrollerProxy of the fund function activateForFund(address _comptrollerProxy, address) external override onlyFeeManager { FeeInfo storage feeInfo = comptrollerProxyToFeeInfo[_comptrollerProxy]; // We must not force asset finality, otherwise funds that have Synths as tracked assets // would be susceptible to a DoS attack when attempting to migrate to a release that uses // this fee: an attacker trades a negligible amount of a tracked Synth with the VaultProxy // as the recipient, thus causing `calcGrossShareValue(true)` to fail. (uint256 grossSharePrice, bool sharePriceIsValid) = ComptrollerLib(_comptrollerProxy) .calcGrossShareValue(false); require(sharePriceIsValid, "activateForFund: Invalid share price"); feeInfo.highWaterMark = grossSharePrice; feeInfo.lastSharePrice = grossSharePrice; feeInfo.activated = block.timestamp; emit ActivatedForFund(_comptrollerProxy, grossSharePrice); } /// @notice Add the initial fee settings for a fund /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @param _settingsData Encoded settings to apply to the policy for the fund /// @dev `highWaterMark`, `lastSharePrice`, and `activated` are set during activation function addFundSettings(address _comptrollerProxy, bytes calldata _settingsData) external override onlyFeeManager { (uint256 feeRate, uint256 feePeriod) = abi.decode(_settingsData, (uint256, uint256)); require(feeRate > 0, "addFundSettings: feeRate must be greater than 0"); require(feePeriod > 0, "addFundSettings: feePeriod must be greater than 0"); comptrollerProxyToFeeInfo[_comptrollerProxy] = FeeInfo({ rate: feeRate, period: feePeriod, activated: 0, lastPaid: 0, highWaterMark: 0, lastSharePrice: 0, aggregateValueDue: 0 }); emit FundSettingsAdded(_comptrollerProxy, feeRate, feePeriod); } /// @notice Provides a constant string identifier for a fee /// @return identifier_ The identifier string function identifier() external pure override returns (string memory identifier_) { return "PERFORMANCE"; } /// @notice Gets the hooks that are implemented by the fee /// @return implementedHooksForSettle_ The hooks during which settle() is implemented /// @return implementedHooksForUpdate_ The hooks during which update() is implemented /// @return usesGavOnSettle_ True if GAV is used during the settle() implementation /// @return usesGavOnUpdate_ True if GAV is used during the update() implementation /// @dev Used only during fee registration function implementedHooks() external view override returns ( IFeeManager.FeeHook[] memory implementedHooksForSettle_, IFeeManager.FeeHook[] memory implementedHooksForUpdate_, bool usesGavOnSettle_, bool usesGavOnUpdate_ ) { implementedHooksForSettle_ = new IFeeManager.FeeHook[](3); implementedHooksForSettle_[0] = IFeeManager.FeeHook.Continuous; implementedHooksForSettle_[1] = IFeeManager.FeeHook.BuySharesSetup; implementedHooksForSettle_[2] = IFeeManager.FeeHook.PreRedeemShares; implementedHooksForUpdate_ = new IFeeManager.FeeHook[](3); implementedHooksForUpdate_[0] = IFeeManager.FeeHook.Continuous; implementedHooksForUpdate_[1] = IFeeManager.FeeHook.BuySharesCompleted; implementedHooksForUpdate_[2] = IFeeManager.FeeHook.PreRedeemShares; return (implementedHooksForSettle_, implementedHooksForUpdate_, true, true); } /// @notice Checks whether the shares outstanding for the fee can be paid out, and updates /// the info for the fee's last payout /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @return isPayable_ True if shares outstanding can be paid out function payout(address _comptrollerProxy, address) external override onlyFeeManager returns (bool isPayable_) { if (!payoutAllowed(_comptrollerProxy)) { return false; } FeeInfo storage feeInfo = comptrollerProxyToFeeInfo[_comptrollerProxy]; feeInfo.lastPaid = block.timestamp; uint256 prevHighWaterMark = feeInfo.highWaterMark; uint256 nextHighWaterMark = __calcUint256Max(feeInfo.lastSharePrice, prevHighWaterMark); uint256 prevAggregateValueDue = feeInfo.aggregateValueDue; // Update state as necessary if (prevAggregateValueDue > 0) { feeInfo.aggregateValueDue = 0; } if (nextHighWaterMark > prevHighWaterMark) { feeInfo.highWaterMark = nextHighWaterMark; } emit PaidOut( _comptrollerProxy, prevHighWaterMark, nextHighWaterMark, prevAggregateValueDue ); return true; } /// @notice Settles the fee and calculates shares due /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @param _vaultProxy The VaultProxy of the fund /// @param _gav The GAV of the fund /// @return settlementType_ The type of settlement /// @return (unused) The payer of shares due /// @return sharesDue_ The amount of shares due function settle( address _comptrollerProxy, address _vaultProxy, IFeeManager.FeeHook, bytes calldata, uint256 _gav ) external override onlyFeeManager returns ( IFeeManager.SettlementType settlementType_, address, uint256 sharesDue_ ) { if (_gav == 0) { return (IFeeManager.SettlementType.None, address(0), 0); } int256 settlementSharesDue = __settleAndUpdatePerformance( _comptrollerProxy, _vaultProxy, _gav ); if (settlementSharesDue == 0) { return (IFeeManager.SettlementType.None, address(0), 0); } else if (settlementSharesDue > 0) { // Settle by minting shares outstanding for custody return ( IFeeManager.SettlementType.MintSharesOutstanding, address(0), uint256(settlementSharesDue) ); } else { // Settle by burning from shares outstanding return ( IFeeManager.SettlementType.BurnSharesOutstanding, address(0), uint256(-settlementSharesDue) ); } } /// @notice Updates the fee state after all fees have finished settle() /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @param _vaultProxy The VaultProxy of the fund /// @param _hook The FeeHook being executed /// @param _settlementData Encoded args to use in calculating the settlement /// @param _gav The GAV of the fund function update( address _comptrollerProxy, address _vaultProxy, IFeeManager.FeeHook _hook, bytes calldata _settlementData, uint256 _gav ) external override onlyFeeManager { uint256 prevSharePrice = comptrollerProxyToFeeInfo[_comptrollerProxy].lastSharePrice; uint256 nextSharePrice = __calcNextSharePrice( _comptrollerProxy, _vaultProxy, _hook, _settlementData, _gav ); if (nextSharePrice == prevSharePrice) { return; } comptrollerProxyToFeeInfo[_comptrollerProxy].lastSharePrice = nextSharePrice; emit LastSharePriceUpdated(_comptrollerProxy, prevSharePrice, nextSharePrice); } // PUBLIC FUNCTIONS /// @notice Checks whether the shares outstanding can be paid out /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @return payoutAllowed_ True if the fee payment is due /// @dev Payout is allowed if fees have not yet been settled in a crystallization period, /// and at least 1 crystallization period has passed since activation function payoutAllowed(address _comptrollerProxy) public view returns (bool payoutAllowed_) { FeeInfo memory feeInfo = comptrollerProxyToFeeInfo[_comptrollerProxy]; uint256 period = feeInfo.period; uint256 timeSinceActivated = block.timestamp.sub(feeInfo.activated); // Check if at least 1 crystallization period has passed since activation if (timeSinceActivated < period) { return false; } // Check that a full crystallization period has passed since the last payout uint256 timeSincePeriodStart = timeSinceActivated % period; uint256 periodStart = block.timestamp.sub(timeSincePeriodStart); return feeInfo.lastPaid < periodStart; } // PRIVATE FUNCTIONS /// @dev Helper to calculate the aggregated value accumulated to a fund since the last /// settlement (happening at investment/redemption) /// Validated: /// _netSharesSupply > 0 /// _sharePriceWithoutPerformance != _prevSharePrice function __calcAggregateValueDue( uint256 _netSharesSupply, uint256 _sharePriceWithoutPerformance, uint256 _prevSharePrice, uint256 _prevAggregateValueDue, uint256 _feeRate, uint256 _highWaterMark ) private pure returns (uint256) { int256 superHWMValueSinceLastSettled = ( int256(__calcUint256Max(_highWaterMark, _sharePriceWithoutPerformance)).sub( int256(__calcUint256Max(_highWaterMark, _prevSharePrice)) ) ) .mul(int256(_netSharesSupply)) .div(int256(SHARE_UNIT)); int256 valueDueSinceLastSettled = superHWMValueSinceLastSettled.mul(int256(_feeRate)).div( int256(RATE_DIVISOR) ); return uint256( __calcInt256Max(0, int256(_prevAggregateValueDue).add(valueDueSinceLastSettled)) ); } /// @dev Helper to calculate the max of two int values function __calcInt256Max(int256 _a, int256 _b) private pure returns (int256) { if (_a >= _b) { return _a; } return _b; } /// @dev Helper to calculate the next `lastSharePrice` value function __calcNextSharePrice( address _comptrollerProxy, address _vaultProxy, IFeeManager.FeeHook _hook, bytes memory _settlementData, uint256 _gav ) private view returns (uint256 nextSharePrice_) { uint256 denominationAssetUnit = 10 ** uint256(ERC20(ComptrollerLib(_comptrollerProxy).getDenominationAsset()).decimals()); if (_gav == 0) { return denominationAssetUnit; } // Get shares outstanding via VaultProxy balance and calc shares supply to get net shares supply ERC20 vaultProxyContract = ERC20(_vaultProxy); uint256 totalSharesSupply = vaultProxyContract.totalSupply(); uint256 nextNetSharesSupply = totalSharesSupply.sub( vaultProxyContract.balanceOf(_vaultProxy) ); if (nextNetSharesSupply == 0) { return denominationAssetUnit; } uint256 nextGav = _gav; // For both Continuous and BuySharesCompleted hooks, _gav and shares supply will not change, // we only need additional calculations for PreRedeemShares if (_hook == IFeeManager.FeeHook.PreRedeemShares) { (, uint256 sharesDecrease) = __decodePreRedeemSharesSettlementData(_settlementData); // Shares have not yet been burned nextNetSharesSupply = nextNetSharesSupply.sub(sharesDecrease); if (nextNetSharesSupply == 0) { return denominationAssetUnit; } // Assets have not yet been withdrawn uint256 gavDecrease = _gav.mul(sharesDecrease).div(totalSharesSupply); nextGav = nextGav.sub(gavDecrease); if (nextGav == 0) { return denominationAssetUnit; } } return nextGav.mul(SHARE_UNIT).div(nextNetSharesSupply); } /// @dev Helper to calculate the performance metrics for a fund. /// Validated: /// _totalSharesSupply > 0 /// _gav > 0 /// _totalSharesSupply != _totalSharesOutstanding function __calcPerformance( address _comptrollerProxy, uint256 _totalSharesSupply, uint256 _totalSharesOutstanding, uint256 _prevAggregateValueDue, FeeInfo memory feeInfo, uint256 _gav ) private view returns (uint256 nextAggregateValueDue_, int256 sharesDue_) { // Use the 'shares supply net shares outstanding' for performance calcs. // Cannot be 0, as _totalSharesSupply != _totalSharesOutstanding uint256 netSharesSupply = _totalSharesSupply.sub(_totalSharesOutstanding); uint256 sharePriceWithoutPerformance = _gav.mul(SHARE_UNIT).div(netSharesSupply); // If gross share price has not changed, can exit early uint256 prevSharePrice = feeInfo.lastSharePrice; if (sharePriceWithoutPerformance == prevSharePrice) { return (_prevAggregateValueDue, 0); } nextAggregateValueDue_ = __calcAggregateValueDue( netSharesSupply, sharePriceWithoutPerformance, prevSharePrice, _prevAggregateValueDue, feeInfo.rate, feeInfo.highWaterMark ); sharesDue_ = __calcSharesDue( _comptrollerProxy, netSharesSupply, _gav, nextAggregateValueDue_ ); return (nextAggregateValueDue_, sharesDue_); } /// @dev Helper to calculate sharesDue during settlement. /// Validated: /// _netSharesSupply > 0 /// _gav > 0 function __calcSharesDue( address _comptrollerProxy, uint256 _netSharesSupply, uint256 _gav, uint256 _nextAggregateValueDue ) private view returns (int256 sharesDue_) { // If _nextAggregateValueDue > _gav, then no shares can be created. // This is a known limitation of the model, which is only reached for unrealistically // high performance fee rates (> 100%). A revert is allowed in such a case. uint256 sharesDueForAggregateValueDue = _nextAggregateValueDue.mul(_netSharesSupply).div( _gav.sub(_nextAggregateValueDue) ); // Shares due is the +/- diff or the total shares outstanding already minted return int256(sharesDueForAggregateValueDue).sub( int256( FeeManager(FEE_MANAGER).getFeeSharesOutstandingForFund( _comptrollerProxy, address(this) ) ) ); } /// @dev Helper to calculate the max of two uint values function __calcUint256Max(uint256 _a, uint256 _b) private pure returns (uint256) { if (_a >= _b) { return _a; } return _b; } /// @dev Helper to settle the fee and update performance state. /// Validated: /// _gav > 0 function __settleAndUpdatePerformance( address _comptrollerProxy, address _vaultProxy, uint256 _gav ) private returns (int256 sharesDue_) { ERC20 sharesTokenContract = ERC20(_vaultProxy); uint256 totalSharesSupply = sharesTokenContract.totalSupply(); if (totalSharesSupply == 0) { return 0; } uint256 totalSharesOutstanding = sharesTokenContract.balanceOf(_vaultProxy); if (totalSharesOutstanding == totalSharesSupply) { return 0; } FeeInfo storage feeInfo = comptrollerProxyToFeeInfo[_comptrollerProxy]; uint256 prevAggregateValueDue = feeInfo.aggregateValueDue; uint256 nextAggregateValueDue; (nextAggregateValueDue, sharesDue_) = __calcPerformance( _comptrollerProxy, totalSharesSupply, totalSharesOutstanding, prevAggregateValueDue, feeInfo, _gav ); if (nextAggregateValueDue == prevAggregateValueDue) { return 0; } // Update fee state feeInfo.aggregateValueDue = nextAggregateValueDue; emit PerformanceUpdated( _comptrollerProxy, prevAggregateValueDue, nextAggregateValueDue, sharesDue_ ); return sharesDue_; } /////////////////// // STATE GETTERS // /////////////////// /// @notice Gets the feeInfo for a given fund /// @param _comptrollerProxy The ComptrollerProxy contract of the fund /// @return feeInfo_ The feeInfo function getFeeInfoForFund(address _comptrollerProxy) external view returns (FeeInfo memory feeInfo_) { return comptrollerProxyToFeeInfo[_comptrollerProxy]; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @title SignedSafeMath * @dev Signed math operations with safety checks that revert on error. */ library SignedSafeMath { int256 constant private _INT256_MIN = -2**255; /** * @dev Returns the multiplication of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(int256 a, int256 b) internal pure returns (int256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow"); int256 c = a * b; require(c / a == b, "SignedSafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two signed integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(int256 a, int256 b) internal pure returns (int256) { require(b != 0, "SignedSafeMath: division by zero"); require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow"); int256 c = a / b; return c; } /** * @dev Returns the subtraction of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(int256 a, int256 b) internal pure returns (int256) { int256 c = a - b; require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); return c; } /** * @dev Returns the addition of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(int256 a, int256 b) internal pure returns (int256) { int256 c = a + b; require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); return c; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/Context.sol"; import "./IERC20.sol"; import "../../math/SafeMath.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name_, string memory symbol_) public { _name = name_; _symbol = symbol_; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal virtual { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./IERC20.sol"; import "../../math/SafeMath.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 SafeMath for uint256; 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' // solhint-disable-next-line max-line-length 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).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @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 * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 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"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping (bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. bytes32 lastvalue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastvalue; // Update the index for the moved value set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { require(set._values.length > index, "EnumerableSet: index out of bounds"); return set._values[index]; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title IDispatcher Interface /// @author Enzyme Council <[email protected]> interface IDispatcher { function cancelMigration(address _vaultProxy, bool _bypassFailure) external; function claimOwnership() external; function deployVaultProxy( address _vaultLib, address _owner, address _vaultAccessor, string calldata _fundName ) external returns (address vaultProxy_); function executeMigration(address _vaultProxy, bool _bypassFailure) external; function getCurrentFundDeployer() external view returns (address currentFundDeployer_); function getFundDeployerForVaultProxy(address _vaultProxy) external view returns (address fundDeployer_); function getMigrationRequestDetailsForVaultProxy(address _vaultProxy) external view returns ( address nextFundDeployer_, address nextVaultAccessor_, address nextVaultLib_, uint256 executableTimestamp_ ); function getMigrationTimelock() external view returns (uint256 migrationTimelock_); function getNominatedOwner() external view returns (address nominatedOwner_); function getOwner() external view returns (address owner_); function getSharesTokenSymbol() external view returns (string memory sharesTokenSymbol_); function getTimelockRemainingForMigrationRequest(address _vaultProxy) external view returns (uint256 secondsRemaining_); function hasExecutableMigrationRequest(address _vaultProxy) external view returns (bool hasExecutableRequest_); function hasMigrationRequest(address _vaultProxy) external view returns (bool hasMigrationRequest_); function removeNominatedOwner() external; function setCurrentFundDeployer(address _nextFundDeployer) external; function setMigrationTimelock(uint256 _nextTimelock) external; function setNominatedOwner(address _nextNominatedOwner) external; function setSharesTokenSymbol(string calldata _nextSymbol) external; function signalMigration( address _vaultProxy, address _nextVaultAccessor, address _nextVaultLib, bool _bypassFailure ) external; }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title IMigratableVault Interface /// @author Enzyme Council <[email protected]> /// @dev DO NOT EDIT CONTRACT interface IMigratableVault { function canMigrate(address _who) external view returns (bool canMigrate_); function init( address _owner, address _accessor, string calldata _fundName ) external; function setAccessor(address _nextAccessor) external; function setVaultLib(address _nextVaultLib) external; }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title IFundDeployer Interface /// @author Enzyme Council <[email protected]> interface IFundDeployer { enum ReleaseStatus {PreLaunch, Live, Paused} function getOwner() external view returns (address); function getReleaseStatus() external view returns (ReleaseStatus); function isRegisteredVaultCall(address, bytes4) external view returns (bool); }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../../../../persistent/dispatcher/IDispatcher.sol"; import "../../../extensions/IExtension.sol"; import "../../../extensions/fee-manager/IFeeManager.sol"; import "../../../extensions/policy-manager/IPolicyManager.sol"; import "../../../infrastructure/price-feeds/primitives/IPrimitivePriceFeed.sol"; import "../../../infrastructure/value-interpreter/IValueInterpreter.sol"; import "../../../utils/AddressArrayLib.sol"; import "../../../utils/AssetFinalityResolver.sol"; import "../../fund-deployer/IFundDeployer.sol"; import "../vault/IVault.sol"; import "./IComptroller.sol"; /// @title ComptrollerLib Contract /// @author Enzyme Council <[email protected]> /// @notice The core logic library shared by all funds contract ComptrollerLib is IComptroller, AssetFinalityResolver { using AddressArrayLib for address[]; using SafeMath for uint256; using SafeERC20 for ERC20; event MigratedSharesDuePaid(uint256 sharesDue); event OverridePauseSet(bool indexed overridePause); event PreRedeemSharesHookFailed( bytes failureReturnData, address redeemer, uint256 sharesQuantity ); event SharesBought( address indexed caller, address indexed buyer, uint256 investmentAmount, uint256 sharesIssued, uint256 sharesReceived ); event SharesRedeemed( address indexed redeemer, uint256 sharesQuantity, address[] receivedAssets, uint256[] receivedAssetQuantities ); event VaultProxySet(address vaultProxy); // Constants and immutables - shared by all proxies uint256 private constant SHARES_UNIT = 10**18; address private immutable DISPATCHER; address private immutable FUND_DEPLOYER; address private immutable FEE_MANAGER; address private immutable INTEGRATION_MANAGER; address private immutable PRIMITIVE_PRICE_FEED; address private immutable POLICY_MANAGER; address private immutable VALUE_INTERPRETER; // Pseudo-constants (can only be set once) address internal denominationAsset; address internal vaultProxy; // True only for the one non-proxy bool internal isLib; // Storage // Allows a fund owner to override a release-level pause bool internal overridePause; // A reverse-mutex, granting atomic permission for particular contracts to make vault calls bool internal permissionedVaultActionAllowed; // A mutex to protect against reentrancy bool internal reentranceLocked; // A timelock between any "shares actions" (i.e., buy and redeem shares), per-account uint256 internal sharesActionTimelock; mapping(address => uint256) internal acctToLastSharesAction; /////////////// // MODIFIERS // /////////////// modifier allowsPermissionedVaultAction { __assertPermissionedVaultActionNotAllowed(); permissionedVaultActionAllowed = true; _; permissionedVaultActionAllowed = false; } modifier locksReentrance() { __assertNotReentranceLocked(); reentranceLocked = true; _; reentranceLocked = false; } modifier onlyActive() { __assertIsActive(vaultProxy); _; } modifier onlyNotPaused() { __assertNotPaused(); _; } modifier onlyFundDeployer() { __assertIsFundDeployer(msg.sender); _; } modifier onlyOwner() { __assertIsOwner(msg.sender); _; } modifier timelockedSharesAction(address _account) { __assertSharesActionNotTimelocked(_account); _; acctToLastSharesAction[_account] = block.timestamp; } // ASSERTION HELPERS // Modifiers are inefficient in terms of contract size, // so we use helper functions to prevent repetitive inlining of expensive string values. /// @dev Since vaultProxy is set during activate(), /// we can check that var rather than storing additional state function __assertIsActive(address _vaultProxy) private pure { require(_vaultProxy != address(0), "Fund not active"); } function __assertIsFundDeployer(address _who) private view { require(_who == FUND_DEPLOYER, "Only FundDeployer callable"); } function __assertIsOwner(address _who) private view { require(_who == IVault(vaultProxy).getOwner(), "Only fund owner callable"); } function __assertLowLevelCall(bool _success, bytes memory _returnData) private pure { require(_success, string(_returnData)); } function __assertNotPaused() private view { require(!__fundIsPaused(), "Fund is paused"); } function __assertNotReentranceLocked() private view { require(!reentranceLocked, "Re-entrance"); } function __assertPermissionedVaultActionNotAllowed() private view { require(!permissionedVaultActionAllowed, "Vault action re-entrance"); } function __assertSharesActionNotTimelocked(address _account) private view { require( block.timestamp.sub(acctToLastSharesAction[_account]) >= sharesActionTimelock, "Shares action timelocked" ); } constructor( address _dispatcher, address _fundDeployer, address _valueInterpreter, address _feeManager, address _integrationManager, address _policyManager, address _primitivePriceFeed, address _synthetixPriceFeed, address _synthetixAddressResolver ) public AssetFinalityResolver(_synthetixPriceFeed, _synthetixAddressResolver) { DISPATCHER = _dispatcher; FEE_MANAGER = _feeManager; FUND_DEPLOYER = _fundDeployer; INTEGRATION_MANAGER = _integrationManager; PRIMITIVE_PRICE_FEED = _primitivePriceFeed; POLICY_MANAGER = _policyManager; VALUE_INTERPRETER = _valueInterpreter; isLib = true; } ///////////// // GENERAL // ///////////// /// @notice Calls a specified action on an Extension /// @param _extension The Extension contract to call (e.g., FeeManager) /// @param _actionId An ID representing the action to take on the extension (see extension) /// @param _callArgs The encoded data for the call /// @dev Used to route arbitrary calls, so that msg.sender is the ComptrollerProxy /// (for access control). Uses a mutex of sorts that allows "permissioned vault actions" /// during calls originating from this function. function callOnExtension( address _extension, uint256 _actionId, bytes calldata _callArgs ) external override onlyNotPaused onlyActive locksReentrance allowsPermissionedVaultAction { require( _extension == FEE_MANAGER || _extension == INTEGRATION_MANAGER, "callOnExtension: _extension invalid" ); IExtension(_extension).receiveCallFromComptroller(msg.sender, _actionId, _callArgs); } /// @notice Sets or unsets an override on a release-wide pause /// @param _nextOverridePause True if the pause should be overrode function setOverridePause(bool _nextOverridePause) external onlyOwner { require(_nextOverridePause != overridePause, "setOverridePause: Value already set"); overridePause = _nextOverridePause; emit OverridePauseSet(_nextOverridePause); } /// @notice Makes an arbitrary call with the VaultProxy contract as the sender /// @param _contract The contract to call /// @param _selector The selector to call /// @param _encodedArgs The encoded arguments for the call function vaultCallOnContract( address _contract, bytes4 _selector, bytes calldata _encodedArgs ) external onlyNotPaused onlyActive onlyOwner { require( IFundDeployer(FUND_DEPLOYER).isRegisteredVaultCall(_contract, _selector), "vaultCallOnContract: Unregistered" ); IVault(vaultProxy).callOnContract(_contract, abi.encodePacked(_selector, _encodedArgs)); } /// @dev Helper to check whether the release is paused, and that there is no local override function __fundIsPaused() private view returns (bool) { return IFundDeployer(FUND_DEPLOYER).getReleaseStatus() == IFundDeployer.ReleaseStatus.Paused && !overridePause; } //////////////////////////////// // PERMISSIONED VAULT ACTIONS // //////////////////////////////// /// @notice Makes a permissioned, state-changing call on the VaultProxy contract /// @param _action The enum representing the VaultAction to perform on the VaultProxy /// @param _actionData The call data for the action to perform function permissionedVaultAction(VaultAction _action, bytes calldata _actionData) external override onlyNotPaused onlyActive { __assertPermissionedVaultAction(msg.sender, _action); if (_action == VaultAction.AddTrackedAsset) { __vaultActionAddTrackedAsset(_actionData); } else if (_action == VaultAction.ApproveAssetSpender) { __vaultActionApproveAssetSpender(_actionData); } else if (_action == VaultAction.BurnShares) { __vaultActionBurnShares(_actionData); } else if (_action == VaultAction.MintShares) { __vaultActionMintShares(_actionData); } else if (_action == VaultAction.RemoveTrackedAsset) { __vaultActionRemoveTrackedAsset(_actionData); } else if (_action == VaultAction.TransferShares) { __vaultActionTransferShares(_actionData); } else if (_action == VaultAction.WithdrawAssetTo) { __vaultActionWithdrawAssetTo(_actionData); } } /// @dev Helper to assert that a caller is allowed to perform a particular VaultAction function __assertPermissionedVaultAction(address _caller, VaultAction _action) private view { require( permissionedVaultActionAllowed, "__assertPermissionedVaultAction: No action allowed" ); if (_caller == INTEGRATION_MANAGER) { require( _action == VaultAction.ApproveAssetSpender || _action == VaultAction.AddTrackedAsset || _action == VaultAction.RemoveTrackedAsset || _action == VaultAction.WithdrawAssetTo, "__assertPermissionedVaultAction: Not valid for IntegrationManager" ); } else if (_caller == FEE_MANAGER) { require( _action == VaultAction.BurnShares || _action == VaultAction.MintShares || _action == VaultAction.TransferShares, "__assertPermissionedVaultAction: Not valid for FeeManager" ); } else { revert("__assertPermissionedVaultAction: Not a valid actor"); } } /// @dev Helper to add a tracked asset to the fund function __vaultActionAddTrackedAsset(bytes memory _actionData) private { address asset = abi.decode(_actionData, (address)); IVault(vaultProxy).addTrackedAsset(asset); } /// @dev Helper to grant a spender an allowance for a fund's asset function __vaultActionApproveAssetSpender(bytes memory _actionData) private { (address asset, address target, uint256 amount) = abi.decode( _actionData, (address, address, uint256) ); IVault(vaultProxy).approveAssetSpender(asset, target, amount); } /// @dev Helper to burn fund shares for a particular account function __vaultActionBurnShares(bytes memory _actionData) private { (address target, uint256 amount) = abi.decode(_actionData, (address, uint256)); IVault(vaultProxy).burnShares(target, amount); } /// @dev Helper to mint fund shares to a particular account function __vaultActionMintShares(bytes memory _actionData) private { (address target, uint256 amount) = abi.decode(_actionData, (address, uint256)); IVault(vaultProxy).mintShares(target, amount); } /// @dev Helper to remove a tracked asset from the fund function __vaultActionRemoveTrackedAsset(bytes memory _actionData) private { address asset = abi.decode(_actionData, (address)); // Allowing this to fail silently makes it cheaper and simpler // for Extensions to not query for the denomination asset if (asset != denominationAsset) { IVault(vaultProxy).removeTrackedAsset(asset); } } /// @dev Helper to transfer fund shares from one account to another function __vaultActionTransferShares(bytes memory _actionData) private { (address from, address to, uint256 amount) = abi.decode( _actionData, (address, address, uint256) ); IVault(vaultProxy).transferShares(from, to, amount); } /// @dev Helper to withdraw an asset from the VaultProxy to a given account function __vaultActionWithdrawAssetTo(bytes memory _actionData) private { (address asset, address target, uint256 amount) = abi.decode( _actionData, (address, address, uint256) ); IVault(vaultProxy).withdrawAssetTo(asset, target, amount); } /////////////// // LIFECYCLE // /////////////// /// @notice Initializes a fund with its core config /// @param _denominationAsset The asset in which the fund's value should be denominated /// @param _sharesActionTimelock The minimum number of seconds between any two "shares actions" /// (buying or selling shares) by the same user /// @dev Pseudo-constructor per proxy. /// No need to assert access because this is called atomically on deployment, /// and once it's called, it cannot be called again. function init(address _denominationAsset, uint256 _sharesActionTimelock) external override { require(denominationAsset == address(0), "init: Already initialized"); require( IPrimitivePriceFeed(PRIMITIVE_PRICE_FEED).isSupportedAsset(_denominationAsset), "init: Bad denomination asset" ); denominationAsset = _denominationAsset; sharesActionTimelock = _sharesActionTimelock; } /// @notice Configure the extensions of a fund /// @param _feeManagerConfigData Encoded config for fees to enable /// @param _policyManagerConfigData Encoded config for policies to enable /// @dev No need to assert anything beyond FundDeployer access. /// Called atomically with init(), but after ComptrollerLib has been deployed, /// giving access to its state and interface function configureExtensions( bytes calldata _feeManagerConfigData, bytes calldata _policyManagerConfigData ) external override onlyFundDeployer { if (_feeManagerConfigData.length > 0) { IExtension(FEE_MANAGER).setConfigForFund(_feeManagerConfigData); } if (_policyManagerConfigData.length > 0) { IExtension(POLICY_MANAGER).setConfigForFund(_policyManagerConfigData); } } /// @notice Activates the fund by attaching a VaultProxy and activating all Extensions /// @param _vaultProxy The VaultProxy to attach to the fund /// @param _isMigration True if a migrated fund is being activated /// @dev No need to assert anything beyond FundDeployer access. function activate(address _vaultProxy, bool _isMigration) external override onlyFundDeployer { vaultProxy = _vaultProxy; emit VaultProxySet(_vaultProxy); if (_isMigration) { // Distribute any shares in the VaultProxy to the fund owner. // This is a mechanism to ensure that even in the edge case of a fund being unable // to payout fee shares owed during migration, these shares are not lost. uint256 sharesDue = ERC20(_vaultProxy).balanceOf(_vaultProxy); if (sharesDue > 0) { IVault(_vaultProxy).transferShares( _vaultProxy, IVault(_vaultProxy).getOwner(), sharesDue ); emit MigratedSharesDuePaid(sharesDue); } } // Note: a future release could consider forcing the adding of a tracked asset here, // just in case a fund is migrating from an old configuration where they are not able // to remove an asset to get under the tracked assets limit IVault(_vaultProxy).addTrackedAsset(denominationAsset); // Activate extensions IExtension(FEE_MANAGER).activateForFund(_isMigration); IExtension(INTEGRATION_MANAGER).activateForFund(_isMigration); IExtension(POLICY_MANAGER).activateForFund(_isMigration); } /// @notice Remove the config for a fund /// @dev No need to assert anything beyond FundDeployer access. /// Calling onlyNotPaused here rather than in the FundDeployer allows /// the owner to potentially override the pause and rescue unpaid fees. function destruct() external override onlyFundDeployer onlyNotPaused allowsPermissionedVaultAction { // Failsafe to protect the libs against selfdestruct require(!isLib, "destruct: Only delegate callable"); // Deactivate the extensions IExtension(FEE_MANAGER).deactivateForFund(); IExtension(INTEGRATION_MANAGER).deactivateForFund(); IExtension(POLICY_MANAGER).deactivateForFund(); // Delete storage of ComptrollerProxy // There should never be ETH in the ComptrollerLib, so no need to waste gas // to get the fund owner selfdestruct(address(0)); } //////////////// // ACCOUNTING // //////////////// /// @notice Calculates the gross asset value (GAV) of the fund /// @param _requireFinality True if all assets must have exact final balances settled /// @return gav_ The fund GAV /// @return isValid_ True if the conversion rates used to derive the GAV are all valid function calcGav(bool _requireFinality) public override returns (uint256 gav_, bool isValid_) { address vaultProxyAddress = vaultProxy; address[] memory assets = IVault(vaultProxyAddress).getTrackedAssets(); if (assets.length == 0) { return (0, true); } uint256[] memory balances = new uint256[](assets.length); for (uint256 i; i < assets.length; i++) { balances[i] = __finalizeIfSynthAndGetAssetBalance( vaultProxyAddress, assets[i], _requireFinality ); } (gav_, isValid_) = IValueInterpreter(VALUE_INTERPRETER).calcCanonicalAssetsTotalValue( assets, balances, denominationAsset ); return (gav_, isValid_); } /// @notice Calculates the gross value of 1 unit of shares in the fund's denomination asset /// @param _requireFinality True if all assets must have exact final balances settled /// @return grossShareValue_ The amount of the denomination asset per share /// @return isValid_ True if the conversion rates to derive the value are all valid /// @dev Does not account for any fees outstanding. function calcGrossShareValue(bool _requireFinality) external override returns (uint256 grossShareValue_, bool isValid_) { uint256 gav; (gav, isValid_) = calcGav(_requireFinality); grossShareValue_ = __calcGrossShareValue( gav, ERC20(vaultProxy).totalSupply(), 10**uint256(ERC20(denominationAsset).decimals()) ); return (grossShareValue_, isValid_); } /// @dev Helper for calculating the gross share value function __calcGrossShareValue( uint256 _gav, uint256 _sharesSupply, uint256 _denominationAssetUnit ) private pure returns (uint256 grossShareValue_) { if (_sharesSupply == 0) { return _denominationAssetUnit; } return _gav.mul(SHARES_UNIT).div(_sharesSupply); } /////////////////// // PARTICIPATION // /////////////////// // BUY SHARES /// @notice Buys shares in the fund for multiple sets of criteria /// @param _buyers The accounts for which to buy shares /// @param _investmentAmounts The amounts of the fund's denomination asset /// with which to buy shares for the corresponding _buyers /// @param _minSharesQuantities The minimum quantities of shares to buy /// with the corresponding _investmentAmounts /// @return sharesReceivedAmounts_ The actual amounts of shares received /// by the corresponding _buyers /// @dev Param arrays have indexes corresponding to individual __buyShares() orders. function buyShares( address[] calldata _buyers, uint256[] calldata _investmentAmounts, uint256[] calldata _minSharesQuantities ) external onlyNotPaused locksReentrance allowsPermissionedVaultAction returns (uint256[] memory sharesReceivedAmounts_) { require(_buyers.length > 0, "buyShares: Empty _buyers"); require( _buyers.length == _investmentAmounts.length && _buyers.length == _minSharesQuantities.length, "buyShares: Unequal arrays" ); address vaultProxyCopy = vaultProxy; __assertIsActive(vaultProxyCopy); require( !IDispatcher(DISPATCHER).hasMigrationRequest(vaultProxyCopy), "buyShares: Pending migration" ); (uint256 gav, bool gavIsValid) = calcGav(true); require(gavIsValid, "buyShares: Invalid GAV"); __buySharesSetupHook(msg.sender, _investmentAmounts, gav); address denominationAssetCopy = denominationAsset; uint256 sharePrice = __calcGrossShareValue( gav, ERC20(vaultProxyCopy).totalSupply(), 10**uint256(ERC20(denominationAssetCopy).decimals()) ); sharesReceivedAmounts_ = new uint256[](_buyers.length); for (uint256 i; i < _buyers.length; i++) { sharesReceivedAmounts_[i] = __buyShares( _buyers[i], _investmentAmounts[i], _minSharesQuantities[i], vaultProxyCopy, sharePrice, gav, denominationAssetCopy ); gav = gav.add(_investmentAmounts[i]); } __buySharesCompletedHook(msg.sender, sharesReceivedAmounts_, gav); return sharesReceivedAmounts_; } /// @dev Helper to buy shares function __buyShares( address _buyer, uint256 _investmentAmount, uint256 _minSharesQuantity, address _vaultProxy, uint256 _sharePrice, uint256 _preBuySharesGav, address _denominationAsset ) private timelockedSharesAction(_buyer) returns (uint256 sharesReceived_) { require(_investmentAmount > 0, "__buyShares: Empty _investmentAmount"); // Gives Extensions a chance to run logic prior to the minting of bought shares __preBuySharesHook(_buyer, _investmentAmount, _minSharesQuantity, _preBuySharesGav); // Calculate the amount of shares to issue with the investment amount uint256 sharesIssued = _investmentAmount.mul(SHARES_UNIT).div(_sharePrice); // Mint shares to the buyer uint256 prevBuyerShares = ERC20(_vaultProxy).balanceOf(_buyer); IVault(_vaultProxy).mintShares(_buyer, sharesIssued); // Transfer the investment asset to the fund. // Does not follow the checks-effects-interactions pattern, but it is preferred // to have the final state of the VaultProxy prior to running __postBuySharesHook(). ERC20(_denominationAsset).safeTransferFrom(msg.sender, _vaultProxy, _investmentAmount); // Gives Extensions a chance to run logic after shares are issued __postBuySharesHook(_buyer, _investmentAmount, sharesIssued, _preBuySharesGav); // The number of actual shares received may differ from shares issued due to // how the PostBuyShares hooks are invoked by Extensions (i.e., fees) sharesReceived_ = ERC20(_vaultProxy).balanceOf(_buyer).sub(prevBuyerShares); require( sharesReceived_ >= _minSharesQuantity, "__buyShares: Shares received < _minSharesQuantity" ); emit SharesBought(msg.sender, _buyer, _investmentAmount, sharesIssued, sharesReceived_); return sharesReceived_; } /// @dev Helper for Extension actions after all __buyShares() calls are made function __buySharesCompletedHook( address _caller, uint256[] memory _sharesReceivedAmounts, uint256 _gav ) private { IPolicyManager(POLICY_MANAGER).validatePolicies( address(this), IPolicyManager.PolicyHook.BuySharesCompleted, abi.encode(_caller, _sharesReceivedAmounts, _gav) ); IFeeManager(FEE_MANAGER).invokeHook( IFeeManager.FeeHook.BuySharesCompleted, abi.encode(_caller, _sharesReceivedAmounts), _gav ); } /// @dev Helper for Extension actions before any __buyShares() calls are made function __buySharesSetupHook( address _caller, uint256[] memory _investmentAmounts, uint256 _gav ) private { IPolicyManager(POLICY_MANAGER).validatePolicies( address(this), IPolicyManager.PolicyHook.BuySharesSetup, abi.encode(_caller, _investmentAmounts, _gav) ); IFeeManager(FEE_MANAGER).invokeHook( IFeeManager.FeeHook.BuySharesSetup, abi.encode(_caller, _investmentAmounts), _gav ); } /// @dev Helper for Extension actions immediately prior to issuing shares. /// This could be cleaned up so both Extensions take the same encoded args and handle GAV /// in the same way, but there is not the obvious need for gas savings of recycling /// the GAV value for the current policies as there is for the fees. function __preBuySharesHook( address _buyer, uint256 _investmentAmount, uint256 _minSharesQuantity, uint256 _gav ) private { IFeeManager(FEE_MANAGER).invokeHook( IFeeManager.FeeHook.PreBuyShares, abi.encode(_buyer, _investmentAmount, _minSharesQuantity), _gav ); IPolicyManager(POLICY_MANAGER).validatePolicies( address(this), IPolicyManager.PolicyHook.PreBuyShares, abi.encode(_buyer, _investmentAmount, _minSharesQuantity, _gav) ); } /// @dev Helper for Extension actions immediately after issuing shares. /// Same comment applies from __preBuySharesHook() above. function __postBuySharesHook( address _buyer, uint256 _investmentAmount, uint256 _sharesIssued, uint256 _preBuySharesGav ) private { uint256 gav = _preBuySharesGav.add(_investmentAmount); IFeeManager(FEE_MANAGER).invokeHook( IFeeManager.FeeHook.PostBuyShares, abi.encode(_buyer, _investmentAmount, _sharesIssued), gav ); IPolicyManager(POLICY_MANAGER).validatePolicies( address(this), IPolicyManager.PolicyHook.PostBuyShares, abi.encode(_buyer, _investmentAmount, _sharesIssued, gav) ); } // REDEEM SHARES /// @notice Redeem all of the sender's shares for a proportionate slice of the fund's assets /// @return payoutAssets_ The assets paid out to the redeemer /// @return payoutAmounts_ The amount of each asset paid out to the redeemer /// @dev See __redeemShares() for further detail function redeemShares() external returns (address[] memory payoutAssets_, uint256[] memory payoutAmounts_) { return __redeemShares( msg.sender, ERC20(vaultProxy).balanceOf(msg.sender), new address[](0), new address[](0) ); } /// @notice Redeem a specified quantity of the sender's shares for a proportionate slice of /// the fund's assets, optionally specifying additional assets and assets to skip. /// @param _sharesQuantity The quantity of shares to redeem /// @param _additionalAssets Additional (non-tracked) assets to claim /// @param _assetsToSkip Tracked assets to forfeit /// @return payoutAssets_ The assets paid out to the redeemer /// @return payoutAmounts_ The amount of each asset paid out to the redeemer /// @dev Any claim to passed _assetsToSkip will be forfeited entirely. This should generally /// only be exercised if a bad asset is causing redemption to fail. function redeemSharesDetailed( uint256 _sharesQuantity, address[] calldata _additionalAssets, address[] calldata _assetsToSkip ) external returns (address[] memory payoutAssets_, uint256[] memory payoutAmounts_) { return __redeemShares(msg.sender, _sharesQuantity, _additionalAssets, _assetsToSkip); } /// @dev Helper to parse an array of payout assets during redemption, taking into account /// additional assets and assets to skip. _assetsToSkip ignores _additionalAssets. /// All input arrays are assumed to be unique. function __parseRedemptionPayoutAssets( address[] memory _trackedAssets, address[] memory _additionalAssets, address[] memory _assetsToSkip ) private pure returns (address[] memory payoutAssets_) { address[] memory trackedAssetsToPayout = _trackedAssets.removeItems(_assetsToSkip); if (_additionalAssets.length == 0) { return trackedAssetsToPayout; } // Add additional assets. Duplicates of trackedAssets are ignored. bool[] memory indexesToAdd = new bool[](_additionalAssets.length); uint256 additionalItemsCount; for (uint256 i; i < _additionalAssets.length; i++) { if (!trackedAssetsToPayout.contains(_additionalAssets[i])) { indexesToAdd[i] = true; additionalItemsCount++; } } if (additionalItemsCount == 0) { return trackedAssetsToPayout; } payoutAssets_ = new address[](trackedAssetsToPayout.length.add(additionalItemsCount)); for (uint256 i; i < trackedAssetsToPayout.length; i++) { payoutAssets_[i] = trackedAssetsToPayout[i]; } uint256 payoutAssetsIndex = trackedAssetsToPayout.length; for (uint256 i; i < _additionalAssets.length; i++) { if (indexesToAdd[i]) { payoutAssets_[payoutAssetsIndex] = _additionalAssets[i]; payoutAssetsIndex++; } } return payoutAssets_; } /// @dev Helper for system actions immediately prior to redeeming shares. /// Policy validation is not currently allowed on redemption, to ensure continuous redeemability. function __preRedeemSharesHook(address _redeemer, uint256 _sharesQuantity) private allowsPermissionedVaultAction { try IFeeManager(FEE_MANAGER).invokeHook( IFeeManager.FeeHook.PreRedeemShares, abi.encode(_redeemer, _sharesQuantity), 0 ) {} catch (bytes memory reason) { emit PreRedeemSharesHookFailed(reason, _redeemer, _sharesQuantity); } } /// @dev Helper to redeem shares. /// This function should never fail without a way to bypass the failure, which is assured /// through two mechanisms: /// 1. The FeeManager is called with the try/catch pattern to assure that calls to it /// can never block redemption. /// 2. If a token fails upon transfer(), that token can be skipped (and its balance forfeited) /// by explicitly specifying _assetsToSkip. /// Because of these assurances, shares should always be redeemable, with the exception /// of the timelock period on shares actions that must be respected. function __redeemShares( address _redeemer, uint256 _sharesQuantity, address[] memory _additionalAssets, address[] memory _assetsToSkip ) private locksReentrance returns (address[] memory payoutAssets_, uint256[] memory payoutAmounts_) { require(_sharesQuantity > 0, "__redeemShares: _sharesQuantity must be >0"); require( _additionalAssets.isUniqueSet(), "__redeemShares: _additionalAssets contains duplicates" ); require(_assetsToSkip.isUniqueSet(), "__redeemShares: _assetsToSkip contains duplicates"); IVault vaultProxyContract = IVault(vaultProxy); // Only apply the sharesActionTimelock when a migration is not pending if (!IDispatcher(DISPATCHER).hasMigrationRequest(address(vaultProxyContract))) { __assertSharesActionNotTimelocked(_redeemer); acctToLastSharesAction[_redeemer] = block.timestamp; } // When a fund is paused, settling fees will be skipped if (!__fundIsPaused()) { // Note that if a fee with `SettlementType.Direct` is charged here (i.e., not `Mint`), // then those fee shares will be transferred from the user's balance rather // than reallocated from the sharesQuantity being redeemed. __preRedeemSharesHook(_redeemer, _sharesQuantity); } // Check the shares quantity against the user's balance after settling fees ERC20 sharesContract = ERC20(address(vaultProxyContract)); require( _sharesQuantity <= sharesContract.balanceOf(_redeemer), "__redeemShares: Insufficient shares" ); // Parse the payout assets given optional params to add or skip assets. // Note that there is no validation that the _additionalAssets are known assets to // the protocol. This means that the redeemer could specify a malicious asset, // but since all state-changing, user-callable functions on this contract share the // non-reentrant modifier, there is nowhere to perform a reentrancy attack. payoutAssets_ = __parseRedemptionPayoutAssets( vaultProxyContract.getTrackedAssets(), _additionalAssets, _assetsToSkip ); require(payoutAssets_.length > 0, "__redeemShares: No payout assets"); // Destroy the shares. // Must get the shares supply before doing so. uint256 sharesSupply = sharesContract.totalSupply(); vaultProxyContract.burnShares(_redeemer, _sharesQuantity); // Calculate and transfer payout asset amounts due to redeemer payoutAmounts_ = new uint256[](payoutAssets_.length); address denominationAssetCopy = denominationAsset; for (uint256 i; i < payoutAssets_.length; i++) { uint256 assetBalance = __finalizeIfSynthAndGetAssetBalance( address(vaultProxyContract), payoutAssets_[i], true ); // If all remaining shares are being redeemed, the logic changes slightly if (_sharesQuantity == sharesSupply) { payoutAmounts_[i] = assetBalance; // Remove every tracked asset, except the denomination asset if (payoutAssets_[i] != denominationAssetCopy) { vaultProxyContract.removeTrackedAsset(payoutAssets_[i]); } } else { payoutAmounts_[i] = assetBalance.mul(_sharesQuantity).div(sharesSupply); } // Transfer payout asset to redeemer if (payoutAmounts_[i] > 0) { vaultProxyContract.withdrawAssetTo(payoutAssets_[i], _redeemer, payoutAmounts_[i]); } } emit SharesRedeemed(_redeemer, _sharesQuantity, payoutAssets_, payoutAmounts_); return (payoutAssets_, payoutAmounts_); } /////////////////// // STATE GETTERS // /////////////////// /// @notice Gets the `denominationAsset` variable /// @return denominationAsset_ The `denominationAsset` variable value function getDenominationAsset() external view override returns (address denominationAsset_) { return denominationAsset; } /// @notice Gets the routes for the various contracts used by all funds /// @return dispatcher_ The `DISPATCHER` variable value /// @return feeManager_ The `FEE_MANAGER` variable value /// @return fundDeployer_ The `FUND_DEPLOYER` variable value /// @return integrationManager_ The `INTEGRATION_MANAGER` variable value /// @return policyManager_ The `POLICY_MANAGER` variable value /// @return primitivePriceFeed_ The `PRIMITIVE_PRICE_FEED` variable value /// @return valueInterpreter_ The `VALUE_INTERPRETER` variable value function getLibRoutes() external view returns ( address dispatcher_, address feeManager_, address fundDeployer_, address integrationManager_, address policyManager_, address primitivePriceFeed_, address valueInterpreter_ ) { return ( DISPATCHER, FEE_MANAGER, FUND_DEPLOYER, INTEGRATION_MANAGER, POLICY_MANAGER, PRIMITIVE_PRICE_FEED, VALUE_INTERPRETER ); } /// @notice Gets the `overridePause` variable /// @return overridePause_ The `overridePause` variable value function getOverridePause() external view returns (bool overridePause_) { return overridePause; } /// @notice Gets the `sharesActionTimelock` variable /// @return sharesActionTimelock_ The `sharesActionTimelock` variable value function getSharesActionTimelock() external view returns (uint256 sharesActionTimelock_) { return sharesActionTimelock; } /// @notice Gets the `vaultProxy` variable /// @return vaultProxy_ The `vaultProxy` variable value function getVaultProxy() external view override returns (address vaultProxy_) { return vaultProxy; } }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title IComptroller Interface /// @author Enzyme Council <[email protected]> interface IComptroller { enum VaultAction { None, BurnShares, MintShares, TransferShares, ApproveAssetSpender, WithdrawAssetTo, AddTrackedAsset, RemoveTrackedAsset } function activate(address, bool) external; function calcGav(bool) external returns (uint256, bool); function calcGrossShareValue(bool) external returns (uint256, bool); function callOnExtension( address, uint256, bytes calldata ) external; function configureExtensions(bytes calldata, bytes calldata) external; function destruct() external; function getDenominationAsset() external view returns (address); function getVaultProxy() external view returns (address); function init(address, uint256) external; function permissionedVaultAction(VaultAction, bytes calldata) external; }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; import "../../../../persistent/utils/IMigratableVault.sol"; /// @title IVault Interface /// @author Enzyme Council <[email protected]> interface IVault is IMigratableVault { function addTrackedAsset(address) external; function approveAssetSpender( address, address, uint256 ) external; function burnShares(address, uint256) external; function callOnContract(address, bytes calldata) external; function getAccessor() external view returns (address); function getOwner() external view returns (address); function getTrackedAssets() external view returns (address[] memory); function isTrackedAsset(address) external view returns (bool); function mintShares(address, uint256) external; function removeTrackedAsset(address) external; function transferShares( address, address, uint256 ) external; function withdrawAssetTo( address, address, uint256 ) external; }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title IExtension Interface /// @author Enzyme Council <[email protected]> /// @notice Interface for all extensions interface IExtension { function activateForFund(bool _isMigration) external; function deactivateForFund() external; function receiveCallFromComptroller( address _comptrollerProxy, uint256 _actionId, bytes calldata _callArgs ) external; function setConfigForFund(bytes calldata _configData) external; }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/utils/EnumerableSet.sol"; import "../../core/fund/comptroller/IComptroller.sol"; import "../../core/fund/vault/IVault.sol"; import "../../utils/AddressArrayLib.sol"; import "../utils/ExtensionBase.sol"; import "../utils/FundDeployerOwnerMixin.sol"; import "../utils/PermissionedVaultActionMixin.sol"; import "./IFee.sol"; import "./IFeeManager.sol"; /// @title FeeManager Contract /// @author Enzyme Council <[email protected]> /// @notice Manages fees for funds contract FeeManager is IFeeManager, ExtensionBase, FundDeployerOwnerMixin, PermissionedVaultActionMixin { using AddressArrayLib for address[]; using EnumerableSet for EnumerableSet.AddressSet; using SafeMath for uint256; event AllSharesOutstandingForcePaidForFund( address indexed comptrollerProxy, address payee, uint256 sharesDue ); event FeeDeregistered(address indexed fee, string indexed identifier); event FeeEnabledForFund( address indexed comptrollerProxy, address indexed fee, bytes settingsData ); event FeeRegistered( address indexed fee, string indexed identifier, FeeHook[] implementedHooksForSettle, FeeHook[] implementedHooksForUpdate, bool usesGavOnSettle, bool usesGavOnUpdate ); event FeeSettledForFund( address indexed comptrollerProxy, address indexed fee, SettlementType indexed settlementType, address payer, address payee, uint256 sharesDue ); event SharesOutstandingPaidForFund( address indexed comptrollerProxy, address indexed fee, uint256 sharesDue ); event FeesRecipientSetForFund( address indexed comptrollerProxy, address prevFeesRecipient, address nextFeesRecipient ); EnumerableSet.AddressSet private registeredFees; mapping(address => bool) private feeToUsesGavOnSettle; mapping(address => bool) private feeToUsesGavOnUpdate; mapping(address => mapping(FeeHook => bool)) private feeToHookToImplementsSettle; mapping(address => mapping(FeeHook => bool)) private feeToHookToImplementsUpdate; mapping(address => address[]) private comptrollerProxyToFees; mapping(address => mapping(address => uint256)) private comptrollerProxyToFeeToSharesOutstanding; constructor(address _fundDeployer) public FundDeployerOwnerMixin(_fundDeployer) {} // EXTERNAL FUNCTIONS /// @notice Activate already-configured fees for use in the calling fund function activateForFund(bool) external override { address vaultProxy = __setValidatedVaultProxy(msg.sender); address[] memory enabledFees = comptrollerProxyToFees[msg.sender]; for (uint256 i; i < enabledFees.length; i++) { IFee(enabledFees[i]).activateForFund(msg.sender, vaultProxy); } } /// @notice Deactivate fees for a fund /// @dev msg.sender is validated during __invokeHook() function deactivateForFund() external override { // Settle continuous fees one last time, but without calling Fee.update() __invokeHook(msg.sender, IFeeManager.FeeHook.Continuous, "", 0, false); // Force payout of remaining shares outstanding __forcePayoutAllSharesOutstanding(msg.sender); // Clean up storage __deleteFundStorage(msg.sender); } /// @notice Receives a dispatched `callOnExtension` from a fund's ComptrollerProxy /// @param _actionId An ID representing the desired action /// @param _callArgs Encoded arguments specific to the _actionId /// @dev This is the only way to call a function on this contract that updates VaultProxy state. /// For both of these actions, any caller is allowed, so we don't use the caller param. function receiveCallFromComptroller( address, uint256 _actionId, bytes calldata _callArgs ) external override { if (_actionId == 0) { // Settle and update all continuous fees __invokeHook(msg.sender, IFeeManager.FeeHook.Continuous, "", 0, true); } else if (_actionId == 1) { __payoutSharesOutstandingForFees(msg.sender, _callArgs); } else { revert("receiveCallFromComptroller: Invalid _actionId"); } } /// @notice Enable and configure fees for use in the calling fund /// @param _configData Encoded config data /// @dev Caller is expected to be a valid ComptrollerProxy, but there isn't a need to validate. /// The order of `fees` determines the order in which fees of the same FeeHook will be applied. /// It is recommended to run ManagementFee before PerformanceFee in order to achieve precise /// PerformanceFee calcs. function setConfigForFund(bytes calldata _configData) external override { (address[] memory fees, bytes[] memory settingsData) = abi.decode( _configData, (address[], bytes[]) ); // Sanity checks require( fees.length == settingsData.length, "setConfigForFund: fees and settingsData array lengths unequal" ); require(fees.isUniqueSet(), "setConfigForFund: fees cannot include duplicates"); // Enable each fee with settings for (uint256 i; i < fees.length; i++) { require(isRegisteredFee(fees[i]), "setConfigForFund: Fee is not registered"); // Set fund config on fee IFee(fees[i]).addFundSettings(msg.sender, settingsData[i]); // Enable fee for fund comptrollerProxyToFees[msg.sender].push(fees[i]); emit FeeEnabledForFund(msg.sender, fees[i], settingsData[i]); } } /// @notice Allows all fees for a particular FeeHook to implement settle() and update() logic /// @param _hook The FeeHook to invoke /// @param _settlementData The encoded settlement parameters specific to the FeeHook /// @param _gav The GAV for a fund if known in the invocating code, otherwise 0 function invokeHook( FeeHook _hook, bytes calldata _settlementData, uint256 _gav ) external override { __invokeHook(msg.sender, _hook, _settlementData, _gav, true); } // PRIVATE FUNCTIONS /// @dev Helper to destroy local storage to get gas refund, /// and to prevent further calls to fee manager function __deleteFundStorage(address _comptrollerProxy) private { delete comptrollerProxyToFees[_comptrollerProxy]; delete comptrollerProxyToVaultProxy[_comptrollerProxy]; } /// @dev Helper to force the payout of shares outstanding across all fees. /// For the current release, all shares in the VaultProxy are assumed to be /// shares outstanding from fees. If not, then they were sent there by mistake /// and are otherwise unrecoverable. We can therefore take the VaultProxy's /// shares balance as the totalSharesOutstanding to payout to the fund owner. function __forcePayoutAllSharesOutstanding(address _comptrollerProxy) private { address vaultProxy = getVaultProxyForFund(_comptrollerProxy); uint256 totalSharesOutstanding = ERC20(vaultProxy).balanceOf(vaultProxy); if (totalSharesOutstanding == 0) { return; } // Destroy any shares outstanding storage address[] memory fees = comptrollerProxyToFees[_comptrollerProxy]; for (uint256 i; i < fees.length; i++) { delete comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][fees[i]]; } // Distribute all shares outstanding to the fees recipient address payee = IVault(vaultProxy).getOwner(); __transferShares(_comptrollerProxy, vaultProxy, payee, totalSharesOutstanding); emit AllSharesOutstandingForcePaidForFund( _comptrollerProxy, payee, totalSharesOutstanding ); } /// @dev Helper to get the canonical value of GAV if not yet set and required by fee function __getGavAsNecessary( address _comptrollerProxy, address _fee, uint256 _gavOrZero ) private returns (uint256 gav_) { if (_gavOrZero == 0 && feeUsesGavOnUpdate(_fee)) { // Assumes that any fee that requires GAV would need to revert if invalid or not final bool gavIsValid; (gav_, gavIsValid) = IComptroller(_comptrollerProxy).calcGav(true); require(gavIsValid, "__getGavAsNecessary: Invalid GAV"); } else { gav_ = _gavOrZero; } return gav_; } /// @dev Helper to run settle() on all enabled fees for a fund that implement a given hook, and then to /// optionally run update() on the same fees. This order allows fees an opportunity to update /// their local state after all VaultProxy state transitions (i.e., minting, burning, /// transferring shares) have finished. To optimize for the expensive operation of calculating /// GAV, once one fee requires GAV, we recycle that `gav` value for subsequent fees. /// Assumes that _gav is either 0 or has already been validated. function __invokeHook( address _comptrollerProxy, FeeHook _hook, bytes memory _settlementData, uint256 _gavOrZero, bool _updateFees ) private { address[] memory fees = comptrollerProxyToFees[_comptrollerProxy]; if (fees.length == 0) { return; } address vaultProxy = getVaultProxyForFund(_comptrollerProxy); // This check isn't strictly necessary, but its cost is insignificant, // and helps to preserve data integrity. require(vaultProxy != address(0), "__invokeHook: Fund is not active"); // First, allow all fees to implement settle() uint256 gav = __settleFees( _comptrollerProxy, vaultProxy, fees, _hook, _settlementData, _gavOrZero ); // Second, allow fees to implement update() // This function does not allow any further altering of VaultProxy state // (i.e., burning, minting, or transferring shares) if (_updateFees) { __updateFees(_comptrollerProxy, vaultProxy, fees, _hook, _settlementData, gav); } } /// @dev Helper to payout the shares outstanding for the specified fees. /// Does not call settle() on fees. /// Only callable via ComptrollerProxy.callOnExtension(). function __payoutSharesOutstandingForFees(address _comptrollerProxy, bytes memory _callArgs) private { address[] memory fees = abi.decode(_callArgs, (address[])); address vaultProxy = getVaultProxyForFund(msg.sender); uint256 sharesOutstandingDue; for (uint256 i; i < fees.length; i++) { if (!IFee(fees[i]).payout(_comptrollerProxy, vaultProxy)) { continue; } uint256 sharesOutstandingForFee = comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][fees[i]]; if (sharesOutstandingForFee == 0) { continue; } sharesOutstandingDue = sharesOutstandingDue.add(sharesOutstandingForFee); // Delete shares outstanding and distribute from VaultProxy to the fees recipient comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][fees[i]] = 0; emit SharesOutstandingPaidForFund(_comptrollerProxy, fees[i], sharesOutstandingForFee); } if (sharesOutstandingDue > 0) { __transferShares( _comptrollerProxy, vaultProxy, IVault(vaultProxy).getOwner(), sharesOutstandingDue ); } } /// @dev Helper to settle a fee function __settleFee( address _comptrollerProxy, address _vaultProxy, address _fee, FeeHook _hook, bytes memory _settlementData, uint256 _gav ) private { (SettlementType settlementType, address payer, uint256 sharesDue) = IFee(_fee).settle( _comptrollerProxy, _vaultProxy, _hook, _settlementData, _gav ); if (settlementType == SettlementType.None) { return; } address payee; if (settlementType == SettlementType.Direct) { payee = IVault(_vaultProxy).getOwner(); __transferShares(_comptrollerProxy, payer, payee, sharesDue); } else if (settlementType == SettlementType.Mint) { payee = IVault(_vaultProxy).getOwner(); __mintShares(_comptrollerProxy, payee, sharesDue); } else if (settlementType == SettlementType.Burn) { __burnShares(_comptrollerProxy, payer, sharesDue); } else if (settlementType == SettlementType.MintSharesOutstanding) { comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][_fee] = comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][_fee] .add(sharesDue); payee = _vaultProxy; __mintShares(_comptrollerProxy, payee, sharesDue); } else if (settlementType == SettlementType.BurnSharesOutstanding) { comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][_fee] = comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][_fee] .sub(sharesDue); payer = _vaultProxy; __burnShares(_comptrollerProxy, payer, sharesDue); } else { revert("__settleFee: Invalid SettlementType"); } emit FeeSettledForFund(_comptrollerProxy, _fee, settlementType, payer, payee, sharesDue); } /// @dev Helper to settle fees that implement a given fee hook function __settleFees( address _comptrollerProxy, address _vaultProxy, address[] memory _fees, FeeHook _hook, bytes memory _settlementData, uint256 _gavOrZero ) private returns (uint256 gav_) { gav_ = _gavOrZero; for (uint256 i; i < _fees.length; i++) { if (!feeSettlesOnHook(_fees[i], _hook)) { continue; } gav_ = __getGavAsNecessary(_comptrollerProxy, _fees[i], gav_); __settleFee(_comptrollerProxy, _vaultProxy, _fees[i], _hook, _settlementData, gav_); } return gav_; } /// @dev Helper to update fees that implement a given fee hook function __updateFees( address _comptrollerProxy, address _vaultProxy, address[] memory _fees, FeeHook _hook, bytes memory _settlementData, uint256 _gavOrZero ) private { uint256 gav = _gavOrZero; for (uint256 i; i < _fees.length; i++) { if (!feeUpdatesOnHook(_fees[i], _hook)) { continue; } gav = __getGavAsNecessary(_comptrollerProxy, _fees[i], gav); IFee(_fees[i]).update(_comptrollerProxy, _vaultProxy, _hook, _settlementData, gav); } } /////////////////// // FEES REGISTRY // /////////////////// /// @notice Remove fees from the list of registered fees /// @param _fees Addresses of fees to be deregistered function deregisterFees(address[] calldata _fees) external onlyFundDeployerOwner { require(_fees.length > 0, "deregisterFees: _fees cannot be empty"); for (uint256 i; i < _fees.length; i++) { require(isRegisteredFee(_fees[i]), "deregisterFees: fee is not registered"); registeredFees.remove(_fees[i]); emit FeeDeregistered(_fees[i], IFee(_fees[i]).identifier()); } } /// @notice Add fees to the list of registered fees /// @param _fees Addresses of fees to be registered /// @dev Stores the hooks that a fee implements and whether each implementation uses GAV, /// which fronts the gas for calls to check if a hook is implemented, and guarantees /// that these hook implementation return values do not change post-registration. function registerFees(address[] calldata _fees) external onlyFundDeployerOwner { require(_fees.length > 0, "registerFees: _fees cannot be empty"); for (uint256 i; i < _fees.length; i++) { require(!isRegisteredFee(_fees[i]), "registerFees: fee already registered"); registeredFees.add(_fees[i]); IFee feeContract = IFee(_fees[i]); ( FeeHook[] memory implementedHooksForSettle, FeeHook[] memory implementedHooksForUpdate, bool usesGavOnSettle, bool usesGavOnUpdate ) = feeContract.implementedHooks(); // Stores the hooks for which each fee implements settle() and update() for (uint256 j; j < implementedHooksForSettle.length; j++) { feeToHookToImplementsSettle[_fees[i]][implementedHooksForSettle[j]] = true; } for (uint256 j; j < implementedHooksForUpdate.length; j++) { feeToHookToImplementsUpdate[_fees[i]][implementedHooksForUpdate[j]] = true; } // Stores whether each fee requires GAV during its implementations for settle() and update() if (usesGavOnSettle) { feeToUsesGavOnSettle[_fees[i]] = true; } if (usesGavOnUpdate) { feeToUsesGavOnUpdate[_fees[i]] = true; } emit FeeRegistered( _fees[i], feeContract.identifier(), implementedHooksForSettle, implementedHooksForUpdate, usesGavOnSettle, usesGavOnUpdate ); } } /////////////////// // STATE GETTERS // /////////////////// /// @notice Get a list of enabled fees for a given fund /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @return enabledFees_ An array of enabled fee addresses function getEnabledFeesForFund(address _comptrollerProxy) external view returns (address[] memory enabledFees_) { return comptrollerProxyToFees[_comptrollerProxy]; } /// @notice Get the amount of shares outstanding for a particular fee for a fund /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @param _fee The fee address /// @return sharesOutstanding_ The amount of shares outstanding function getFeeSharesOutstandingForFund(address _comptrollerProxy, address _fee) external view returns (uint256 sharesOutstanding_) { return comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][_fee]; } /// @notice Get all registered fees /// @return registeredFees_ A list of all registered fee addresses function getRegisteredFees() external view returns (address[] memory registeredFees_) { registeredFees_ = new address[](registeredFees.length()); for (uint256 i; i < registeredFees_.length; i++) { registeredFees_[i] = registeredFees.at(i); } return registeredFees_; } /// @notice Checks if a fee implements settle() on a particular hook /// @param _fee The address of the fee to check /// @param _hook The FeeHook to check /// @return settlesOnHook_ True if the fee settles on the given hook function feeSettlesOnHook(address _fee, FeeHook _hook) public view returns (bool settlesOnHook_) { return feeToHookToImplementsSettle[_fee][_hook]; } /// @notice Checks if a fee implements update() on a particular hook /// @param _fee The address of the fee to check /// @param _hook The FeeHook to check /// @return updatesOnHook_ True if the fee updates on the given hook function feeUpdatesOnHook(address _fee, FeeHook _hook) public view returns (bool updatesOnHook_) { return feeToHookToImplementsUpdate[_fee][_hook]; } /// @notice Checks if a fee uses GAV in its settle() implementation /// @param _fee The address of the fee to check /// @return usesGav_ True if the fee uses GAV during settle() implementation function feeUsesGavOnSettle(address _fee) public view returns (bool usesGav_) { return feeToUsesGavOnSettle[_fee]; } /// @notice Checks if a fee uses GAV in its update() implementation /// @param _fee The address of the fee to check /// @return usesGav_ True if the fee uses GAV during update() implementation function feeUsesGavOnUpdate(address _fee) public view returns (bool usesGav_) { return feeToUsesGavOnUpdate[_fee]; } /// @notice Check whether a fee is registered /// @param _fee The address of the fee to check /// @return isRegisteredFee_ True if the fee is registered function isRegisteredFee(address _fee) public view returns (bool isRegisteredFee_) { return registeredFees.contains(_fee); } }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; import "./IFeeManager.sol"; /// @title Fee Interface /// @author Enzyme Council <[email protected]> /// @notice Interface for all fees interface IFee { function activateForFund(address _comptrollerProxy, address _vaultProxy) external; function addFundSettings(address _comptrollerProxy, bytes calldata _settingsData) external; function identifier() external pure returns (string memory identifier_); function implementedHooks() external view returns ( IFeeManager.FeeHook[] memory implementedHooksForSettle_, IFeeManager.FeeHook[] memory implementedHooksForUpdate_, bool usesGavOnSettle_, bool usesGavOnUpdate_ ); function payout(address _comptrollerProxy, address _vaultProxy) external returns (bool isPayable_); function settle( address _comptrollerProxy, address _vaultProxy, IFeeManager.FeeHook _hook, bytes calldata _settlementData, uint256 _gav ) external returns ( IFeeManager.SettlementType settlementType_, address payer_, uint256 sharesDue_ ); function update( address _comptrollerProxy, address _vaultProxy, IFeeManager.FeeHook _hook, bytes calldata _settlementData, uint256 _gav ) external; }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; pragma experimental ABIEncoderV2; /// @title FeeManager Interface /// @author Enzyme Council <[email protected]> /// @notice Interface for the FeeManager interface IFeeManager { // No fees for the current release are implemented post-redeemShares enum FeeHook { Continuous, BuySharesSetup, PreBuyShares, PostBuyShares, BuySharesCompleted, PreRedeemShares } enum SettlementType {None, Direct, Mint, Burn, MintSharesOutstanding, BurnSharesOutstanding} function invokeHook( FeeHook, bytes calldata, uint256 ) external; }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; import "../../IFee.sol"; /// @title FeeBase Contract /// @author Enzyme Council <[email protected]> /// @notice Abstract base contract for all fees abstract contract FeeBase is IFee { address internal immutable FEE_MANAGER; modifier onlyFeeManager { require(msg.sender == FEE_MANAGER, "Only the FeeManger can make this call"); _; } constructor(address _feeManager) public { FEE_MANAGER = _feeManager; } /// @notice Allows Fee to run logic during fund activation /// @dev Unimplemented by default, may be overrode. function activateForFund(address, address) external virtual override { return; } /// @notice Runs payout logic for a fee that utilizes shares outstanding as its settlement type /// @dev Returns false by default, can be overridden by fee function payout(address, address) external virtual override returns (bool) { return false; } /// @notice Update fee state after all settlement has occurred during a given fee hook /// @dev Unimplemented by default, can be overridden by fee function update( address, address, IFeeManager.FeeHook, bytes calldata, uint256 ) external virtual override { return; } /// @notice Helper to parse settlement arguments from encoded data for PreBuyShares fee hook function __decodePreBuySharesSettlementData(bytes memory _settlementData) internal pure returns ( address buyer_, uint256 investmentAmount_, uint256 minSharesQuantity_ ) { return abi.decode(_settlementData, (address, uint256, uint256)); } /// @notice Helper to parse settlement arguments from encoded data for PreRedeemShares fee hook function __decodePreRedeemSharesSettlementData(bytes memory _settlementData) internal pure returns (address redeemer_, uint256 sharesQuantity_) { return abi.decode(_settlementData, (address, uint256)); } /// @notice Helper to parse settlement arguments from encoded data for PostBuyShares fee hook function __decodePostBuySharesSettlementData(bytes memory _settlementData) internal pure returns ( address buyer_, uint256 investmentAmount_, uint256 sharesBought_ ) { return abi.decode(_settlementData, (address, uint256, uint256)); } /////////////////// // STATE GETTERS // /////////////////// /// @notice Gets the `FEE_MANAGER` variable /// @return feeManager_ The `FEE_MANAGER` variable value function getFeeManager() external view returns (address feeManager_) { return FEE_MANAGER; } }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; pragma experimental ABIEncoderV2; /// @title PolicyManager Interface /// @author Enzyme Council <[email protected]> /// @notice Interface for the PolicyManager interface IPolicyManager { enum PolicyHook { BuySharesSetup, PreBuyShares, PostBuyShares, BuySharesCompleted, PreCallOnIntegration, PostCallOnIntegration } function validatePolicies( address, PolicyHook, bytes calldata ) external; }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; import "../../core/fund/comptroller/IComptroller.sol"; import "../../core/fund/vault/IVault.sol"; import "../IExtension.sol"; /// @title ExtensionBase Contract /// @author Enzyme Council <[email protected]> /// @notice Base class for an extension abstract contract ExtensionBase is IExtension { mapping(address => address) internal comptrollerProxyToVaultProxy; /// @notice Allows extension to run logic during fund activation /// @dev Unimplemented by default, may be overridden. function activateForFund(bool) external virtual override { return; } /// @notice Allows extension to run logic during fund deactivation (destruct) /// @dev Unimplemented by default, may be overridden. function deactivateForFund() external virtual override { return; } /// @notice Receives calls from ComptrollerLib.callOnExtension() /// and dispatches the appropriate action /// @dev Unimplemented by default, may be overridden. function receiveCallFromComptroller( address, uint256, bytes calldata ) external virtual override { revert("receiveCallFromComptroller: Unimplemented for Extension"); } /// @notice Allows extension to run logic during fund configuration /// @dev Unimplemented by default, may be overridden. function setConfigForFund(bytes calldata) external virtual override { return; } /// @dev Helper to validate a ComptrollerProxy-VaultProxy relation, which we store for both /// gas savings and to guarantee a spoofed ComptrollerProxy does not change getVaultProxy(). /// Will revert without reason if the expected interfaces do not exist. function __setValidatedVaultProxy(address _comptrollerProxy) internal returns (address vaultProxy_) { require( comptrollerProxyToVaultProxy[_comptrollerProxy] == address(0), "__setValidatedVaultProxy: Already set" ); vaultProxy_ = IComptroller(_comptrollerProxy).getVaultProxy(); require(vaultProxy_ != address(0), "__setValidatedVaultProxy: Missing vaultProxy"); require( _comptrollerProxy == IVault(vaultProxy_).getAccessor(), "__setValidatedVaultProxy: Not the VaultProxy accessor" ); comptrollerProxyToVaultProxy[_comptrollerProxy] = vaultProxy_; return vaultProxy_; } /////////////////// // STATE GETTERS // /////////////////// /// @notice Gets the verified VaultProxy for a given ComptrollerProxy /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @return vaultProxy_ The VaultProxy of the fund function getVaultProxyForFund(address _comptrollerProxy) public view returns (address vaultProxy_) { return comptrollerProxyToVaultProxy[_comptrollerProxy]; } }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; import "../../core/fund-deployer/IFundDeployer.sol"; /// @title FundDeployerOwnerMixin Contract /// @author Enzyme Council <[email protected]> /// @notice A mixin contract that defers ownership to the owner of FundDeployer abstract contract FundDeployerOwnerMixin { address internal immutable FUND_DEPLOYER; modifier onlyFundDeployerOwner() { require( msg.sender == getOwner(), "onlyFundDeployerOwner: Only the FundDeployer owner can call this function" ); _; } constructor(address _fundDeployer) public { FUND_DEPLOYER = _fundDeployer; } /// @notice Gets the owner of this contract /// @return owner_ The owner /// @dev Ownership is deferred to the owner of the FundDeployer contract function getOwner() public view returns (address owner_) { return IFundDeployer(FUND_DEPLOYER).getOwner(); } /////////////////// // STATE GETTERS // /////////////////// /// @notice Gets the `FUND_DEPLOYER` variable /// @return fundDeployer_ The `FUND_DEPLOYER` variable value function getFundDeployer() external view returns (address fundDeployer_) { return FUND_DEPLOYER; } }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; import "../../core/fund/comptroller/IComptroller.sol"; /// @title PermissionedVaultActionMixin Contract /// @author Enzyme Council <[email protected]> /// @notice A mixin contract for extensions that can make permissioned vault calls abstract contract PermissionedVaultActionMixin { /// @notice Adds a tracked asset to the fund /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @param _asset The asset to add function __addTrackedAsset(address _comptrollerProxy, address _asset) internal { IComptroller(_comptrollerProxy).permissionedVaultAction( IComptroller.VaultAction.AddTrackedAsset, abi.encode(_asset) ); } /// @notice Grants an allowance to a spender to use a fund's asset /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @param _asset The asset for which to grant an allowance /// @param _target The spender of the allowance /// @param _amount The amount of the allowance function __approveAssetSpender( address _comptrollerProxy, address _asset, address _target, uint256 _amount ) internal { IComptroller(_comptrollerProxy).permissionedVaultAction( IComptroller.VaultAction.ApproveAssetSpender, abi.encode(_asset, _target, _amount) ); } /// @notice Burns fund shares for a particular account /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @param _target The account for which to burn shares /// @param _amount The amount of shares to burn function __burnShares( address _comptrollerProxy, address _target, uint256 _amount ) internal { IComptroller(_comptrollerProxy).permissionedVaultAction( IComptroller.VaultAction.BurnShares, abi.encode(_target, _amount) ); } /// @notice Mints fund shares to a particular account /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @param _target The account to which to mint shares /// @param _amount The amount of shares to mint function __mintShares( address _comptrollerProxy, address _target, uint256 _amount ) internal { IComptroller(_comptrollerProxy).permissionedVaultAction( IComptroller.VaultAction.MintShares, abi.encode(_target, _amount) ); } /// @notice Removes a tracked asset from the fund /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @param _asset The asset to remove function __removeTrackedAsset(address _comptrollerProxy, address _asset) internal { IComptroller(_comptrollerProxy).permissionedVaultAction( IComptroller.VaultAction.RemoveTrackedAsset, abi.encode(_asset) ); } /// @notice Transfers fund shares from one account to another /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @param _from The account from which to transfer shares /// @param _to The account to which to transfer shares /// @param _amount The amount of shares to transfer function __transferShares( address _comptrollerProxy, address _from, address _to, uint256 _amount ) internal { IComptroller(_comptrollerProxy).permissionedVaultAction( IComptroller.VaultAction.TransferShares, abi.encode(_from, _to, _amount) ); } /// @notice Withdraws an asset from the VaultProxy to a given account /// @param _comptrollerProxy The ComptrollerProxy of the fund /// @param _asset The asset to withdraw /// @param _target The account to which to withdraw the asset /// @param _amount The amount of asset to withdraw function __withdrawAssetTo( address _comptrollerProxy, address _asset, address _target, uint256 _amount ) internal { IComptroller(_comptrollerProxy).permissionedVaultAction( IComptroller.VaultAction.WithdrawAssetTo, abi.encode(_asset, _target, _amount) ); } }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title IDerivativePriceFeed Interface /// @author Enzyme Council <[email protected]> /// @notice Simple interface for derivative price source oracle implementations interface IDerivativePriceFeed { function calcUnderlyingValues(address, uint256) external returns (address[] memory, uint256[] memory); function isSupportedAsset(address) external view returns (bool); }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../../../../extensions/utils/FundDeployerOwnerMixin.sol"; import "../../../../interfaces/ISynthetix.sol"; import "../../../../interfaces/ISynthetixAddressResolver.sol"; import "../../../../interfaces/ISynthetixExchangeRates.sol"; import "../../../../interfaces/ISynthetixProxyERC20.sol"; import "../../../../interfaces/ISynthetixSynth.sol"; import "../IDerivativePriceFeed.sol"; /// @title SynthetixPriceFeed Contract /// @author Enzyme Council <[email protected]> /// @notice A price feed that uses Synthetix oracles as price sources contract SynthetixPriceFeed is IDerivativePriceFeed, FundDeployerOwnerMixin { using SafeMath for uint256; event SynthAdded(address indexed synth, bytes32 currencyKey); event SynthCurrencyKeyUpdated( address indexed synth, bytes32 prevCurrencyKey, bytes32 nextCurrencyKey ); uint256 private constant SYNTH_UNIT = 10**18; address private immutable ADDRESS_RESOLVER; address private immutable SUSD; mapping(address => bytes32) private synthToCurrencyKey; constructor( address _fundDeployer, address _addressResolver, address _sUSD, address[] memory _synths ) public FundDeployerOwnerMixin(_fundDeployer) { ADDRESS_RESOLVER = _addressResolver; SUSD = _sUSD; address[] memory sUSDSynths = new address[](1); sUSDSynths[0] = _sUSD; __addSynths(sUSDSynths); __addSynths(_synths); } /// @notice Converts a given amount of a derivative to its underlying asset values /// @param _derivative The derivative to convert /// @param _derivativeAmount The amount of the derivative to convert /// @return underlyings_ The underlying assets for the _derivative /// @return underlyingAmounts_ The amount of each underlying asset for the equivalent derivative amount function calcUnderlyingValues(address _derivative, uint256 _derivativeAmount) external override returns (address[] memory underlyings_, uint256[] memory underlyingAmounts_) { underlyings_ = new address[](1); underlyings_[0] = SUSD; underlyingAmounts_ = new uint256[](1); bytes32 currencyKey = getCurrencyKeyForSynth(_derivative); require(currencyKey != 0, "calcUnderlyingValues: _derivative is not supported"); address exchangeRates = ISynthetixAddressResolver(ADDRESS_RESOLVER).requireAndGetAddress( "ExchangeRates", "calcUnderlyingValues: Missing ExchangeRates" ); (uint256 rate, bool isInvalid) = ISynthetixExchangeRates(exchangeRates).rateAndInvalid( currencyKey ); require(!isInvalid, "calcUnderlyingValues: _derivative rate is not valid"); underlyingAmounts_[0] = _derivativeAmount.mul(rate).div(SYNTH_UNIT); return (underlyings_, underlyingAmounts_); } /// @notice Checks whether an asset is a supported primitive of the price feed /// @param _asset The asset to check /// @return isSupported_ True if the asset is a supported primitive function isSupportedAsset(address _asset) public view override returns (bool isSupported_) { return getCurrencyKeyForSynth(_asset) != 0; } ///////////////////// // SYNTHS REGISTRY // ///////////////////// /// @notice Adds Synths to the price feed /// @param _synths Synths to add function addSynths(address[] calldata _synths) external onlyFundDeployerOwner { require(_synths.length > 0, "addSynths: Empty _synths"); __addSynths(_synths); } /// @notice Updates the cached currencyKey value for specified Synths /// @param _synths Synths to update /// @dev Anybody can call this function function updateSynthCurrencyKeys(address[] calldata _synths) external { require(_synths.length > 0, "updateSynthCurrencyKeys: Empty _synths"); for (uint256 i; i < _synths.length; i++) { bytes32 prevCurrencyKey = synthToCurrencyKey[_synths[i]]; require(prevCurrencyKey != 0, "updateSynthCurrencyKeys: Synth not set"); bytes32 nextCurrencyKey = __getCurrencyKey(_synths[i]); require( nextCurrencyKey != prevCurrencyKey, "updateSynthCurrencyKeys: Synth has correct currencyKey" ); synthToCurrencyKey[_synths[i]] = nextCurrencyKey; emit SynthCurrencyKeyUpdated(_synths[i], prevCurrencyKey, nextCurrencyKey); } } /// @dev Helper to add Synths function __addSynths(address[] memory _synths) private { for (uint256 i; i < _synths.length; i++) { require(synthToCurrencyKey[_synths[i]] == 0, "__addSynths: Value already set"); bytes32 currencyKey = __getCurrencyKey(_synths[i]); require(currencyKey != 0, "__addSynths: No currencyKey"); synthToCurrencyKey[_synths[i]] = currencyKey; emit SynthAdded(_synths[i], currencyKey); } } /// @dev Helper to query a currencyKey from Synthetix function __getCurrencyKey(address _synthProxy) private view returns (bytes32 currencyKey_) { return ISynthetixSynth(ISynthetixProxyERC20(_synthProxy).target()).currencyKey(); } /////////////////// // STATE GETTERS // /////////////////// /// @notice Gets the `ADDRESS_RESOLVER` variable /// @return addressResolver_ The `ADDRESS_RESOLVER` variable value function getAddressResolver() external view returns (address) { return ADDRESS_RESOLVER; } /// @notice Gets the currencyKey for multiple given Synths /// @return currencyKeys_ The currencyKey values function getCurrencyKeysForSynths(address[] calldata _synths) external view returns (bytes32[] memory currencyKeys_) { currencyKeys_ = new bytes32[](_synths.length); for (uint256 i; i < _synths.length; i++) { currencyKeys_[i] = synthToCurrencyKey[_synths[i]]; } return currencyKeys_; } /// @notice Gets the `SUSD` variable /// @return susd_ The `SUSD` variable value function getSUSD() external view returns (address susd_) { return SUSD; } /// @notice Gets the currencyKey for a given Synth /// @return currencyKey_ The currencyKey value function getCurrencyKeyForSynth(address _synth) public view returns (bytes32 currencyKey_) { return synthToCurrencyKey[_synth]; } }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title IPrimitivePriceFeed Interface /// @author Enzyme Council <[email protected]> /// @notice Interface for primitive price feeds interface IPrimitivePriceFeed { function calcCanonicalValue( address, uint256, address ) external view returns (uint256, bool); function calcLiveValue( address, uint256, address ) external view returns (uint256, bool); function isSupportedAsset(address) external view returns (bool); }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title IValueInterpreter interface /// @author Enzyme Council <[email protected]> /// @notice Interface for ValueInterpreter interface IValueInterpreter { function calcCanonicalAssetValue( address, uint256, address ) external returns (uint256, bool); function calcCanonicalAssetsTotalValue( address[] calldata, uint256[] calldata, address ) external returns (uint256, bool); function calcLiveAssetValue( address, uint256, address ) external returns (uint256, bool); function calcLiveAssetsTotalValue( address[] calldata, uint256[] calldata, address ) external returns (uint256, bool); }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title ISynthetix Interface /// @author Enzyme Council <[email protected]> interface ISynthetix { function exchangeOnBehalfWithTracking( address, bytes32, uint256, bytes32, address, bytes32 ) external returns (uint256); }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title ISynthetixAddressResolver Interface /// @author Enzyme Council <[email protected]> interface ISynthetixAddressResolver { function requireAndGetAddress(bytes32, string calldata) external view returns (address); }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title ISynthetixExchangeRates Interface /// @author Enzyme Council <[email protected]> interface ISynthetixExchangeRates { function rateAndInvalid(bytes32) external view returns (uint256, bool); }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title ISynthetixExchanger Interface /// @author Enzyme Council <[email protected]> interface ISynthetixExchanger { function getAmountsForExchange( uint256, bytes32, bytes32 ) external view returns ( uint256, uint256, uint256 ); function settle(address, bytes32) external returns ( uint256, uint256, uint256 ); }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title ISynthetixProxyERC20 Interface /// @author Enzyme Council <[email protected]> interface ISynthetixProxyERC20 { function target() external view returns (address); }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title ISynthetixSynth Interface /// @author Enzyme Council <[email protected]> interface ISynthetixSynth { function currencyKey() external view returns (bytes32); }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; /// @title AddressArray Library /// @author Enzyme Council <[email protected]> /// @notice A library to extend the address array data type library AddressArrayLib { /// @dev Helper to add an item to an array. Does not assert uniqueness of the new item. function addItem(address[] memory _self, address _itemToAdd) internal pure returns (address[] memory nextArray_) { nextArray_ = new address[](_self.length + 1); for (uint256 i; i < _self.length; i++) { nextArray_[i] = _self[i]; } nextArray_[_self.length] = _itemToAdd; return nextArray_; } /// @dev Helper to add an item to an array, only if it is not already in the array. function addUniqueItem(address[] memory _self, address _itemToAdd) internal pure returns (address[] memory nextArray_) { if (contains(_self, _itemToAdd)) { return _self; } return addItem(_self, _itemToAdd); } /// @dev Helper to verify if an array contains a particular value function contains(address[] memory _self, address _target) internal pure returns (bool doesContain_) { for (uint256 i; i < _self.length; i++) { if (_target == _self[i]) { return true; } } return false; } /// @dev Helper to reassign all items in an array with a specified value function fill(address[] memory _self, address _value) internal pure returns (address[] memory nextArray_) { nextArray_ = new address[](_self.length); for (uint256 i; i < nextArray_.length; i++) { nextArray_[i] = _value; } return nextArray_; } /// @dev Helper to verify if array is a set of unique values. /// Does not assert length > 0. function isUniqueSet(address[] memory _self) internal pure returns (bool isUnique_) { if (_self.length <= 1) { return true; } uint256 arrayLength = _self.length; for (uint256 i; i < arrayLength; i++) { for (uint256 j = i + 1; j < arrayLength; j++) { if (_self[i] == _self[j]) { return false; } } } return true; } /// @dev Helper to remove items from an array. Removes all matching occurrences of each item. /// Does not assert uniqueness of either array. function removeItems(address[] memory _self, address[] memory _itemsToRemove) internal pure returns (address[] memory nextArray_) { if (_itemsToRemove.length == 0) { return _self; } bool[] memory indexesToRemove = new bool[](_self.length); uint256 remainingItemsCount = _self.length; for (uint256 i; i < _self.length; i++) { if (contains(_itemsToRemove, _self[i])) { indexesToRemove[i] = true; remainingItemsCount--; } } if (remainingItemsCount == _self.length) { nextArray_ = _self; } else if (remainingItemsCount > 0) { nextArray_ = new address[](remainingItemsCount); uint256 nextArrayIndex; for (uint256 i; i < _self.length; i++) { if (!indexesToRemove[i]) { nextArray_[nextArrayIndex] = _self[i]; nextArrayIndex++; } } } return nextArray_; } }
// SPDX-License-Identifier: GPL-3.0 /* This file is part of the Enzyme Protocol. (c) Enzyme Council <[email protected]> For the full license information, please view the LICENSE file that was distributed with this source code. */ pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "../infrastructure/price-feeds/derivatives/feeds/SynthetixPriceFeed.sol"; import "../interfaces/ISynthetixAddressResolver.sol"; import "../interfaces/ISynthetixExchanger.sol"; /// @title AssetFinalityResolver Contract /// @author Enzyme Council <[email protected]> /// @notice A contract that helps achieve asset finality abstract contract AssetFinalityResolver { address internal immutable SYNTHETIX_ADDRESS_RESOLVER; address internal immutable SYNTHETIX_PRICE_FEED; constructor(address _synthetixPriceFeed, address _synthetixAddressResolver) public { SYNTHETIX_ADDRESS_RESOLVER = _synthetixAddressResolver; SYNTHETIX_PRICE_FEED = _synthetixPriceFeed; } /// @dev Helper to finalize a Synth balance at a given target address and return its balance function __finalizeIfSynthAndGetAssetBalance( address _target, address _asset, bool _requireFinality ) internal returns (uint256 assetBalance_) { bytes32 currencyKey = SynthetixPriceFeed(SYNTHETIX_PRICE_FEED).getCurrencyKeyForSynth( _asset ); if (currencyKey != 0) { address synthetixExchanger = ISynthetixAddressResolver(SYNTHETIX_ADDRESS_RESOLVER) .requireAndGetAddress( "Exchanger", "finalizeAndGetAssetBalance: Missing Exchanger" ); try ISynthetixExchanger(synthetixExchanger).settle(_target, currencyKey) {} catch { require(!_requireFinality, "finalizeAndGetAssetBalance: Cannot settle Synth"); } } return ERC20(_asset).balanceOf(_target); } /////////////////// // STATE GETTERS // /////////////////// /// @notice Gets the `SYNTHETIX_ADDRESS_RESOLVER` variable /// @return synthetixAddressResolver_ The `SYNTHETIX_ADDRESS_RESOLVER` variable value function getSynthetixAddressResolver() external view returns (address synthetixAddressResolver_) { return SYNTHETIX_ADDRESS_RESOLVER; } /// @notice Gets the `SYNTHETIX_PRICE_FEED` variable /// @return synthetixPriceFeed_ The `SYNTHETIX_PRICE_FEED` variable value function getSynthetixPriceFeed() external view returns (address synthetixPriceFeed_) { return SYNTHETIX_PRICE_FEED; } }
{ "evmVersion": "istanbul", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "details": { "constantOptimizer": true, "cse": true, "deduplicate": true, "jumpdestRemover": true, "orderLiterals": true, "peephole": true, "yul": false }, "runs": 200 }, "remappings": [], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_feeManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"comptrollerProxy","type":"address"},{"indexed":false,"internalType":"uint256","name":"highWaterMark","type":"uint256"}],"name":"ActivatedForFund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"comptrollerProxy","type":"address"},{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"period","type":"uint256"}],"name":"FundSettingsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"comptrollerProxy","type":"address"},{"indexed":false,"internalType":"uint256","name":"prevSharePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nextSharePrice","type":"uint256"}],"name":"LastSharePriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"comptrollerProxy","type":"address"},{"indexed":false,"internalType":"uint256","name":"prevHighWaterMark","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nextHighWaterMark","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"aggregateValueDue","type":"uint256"}],"name":"PaidOut","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"comptrollerProxy","type":"address"},{"indexed":false,"internalType":"uint256","name":"prevAggregateValueDue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nextAggregateValueDue","type":"uint256"},{"indexed":false,"internalType":"int256","name":"sharesOutstandingDiff","type":"int256"}],"name":"PerformanceUpdated","type":"event"},{"inputs":[{"internalType":"address","name":"_comptrollerProxy","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"activateForFund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_comptrollerProxy","type":"address"},{"internalType":"bytes","name":"_settingsData","type":"bytes"}],"name":"addFundSettings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_comptrollerProxy","type":"address"}],"name":"getFeeInfoForFund","outputs":[{"components":[{"internalType":"uint256","name":"rate","type":"uint256"},{"internalType":"uint256","name":"period","type":"uint256"},{"internalType":"uint256","name":"activated","type":"uint256"},{"internalType":"uint256","name":"lastPaid","type":"uint256"},{"internalType":"uint256","name":"highWaterMark","type":"uint256"},{"internalType":"uint256","name":"lastSharePrice","type":"uint256"},{"internalType":"uint256","name":"aggregateValueDue","type":"uint256"}],"internalType":"struct PerformanceFee.FeeInfo","name":"feeInfo_","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeeManager","outputs":[{"internalType":"address","name":"feeManager_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"identifier","outputs":[{"internalType":"string","name":"identifier_","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"implementedHooks","outputs":[{"internalType":"enum IFeeManager.FeeHook[]","name":"implementedHooksForSettle_","type":"uint8[]"},{"internalType":"enum IFeeManager.FeeHook[]","name":"implementedHooksForUpdate_","type":"uint8[]"},{"internalType":"bool","name":"usesGavOnSettle_","type":"bool"},{"internalType":"bool","name":"usesGavOnUpdate_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_comptrollerProxy","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"payout","outputs":[{"internalType":"bool","name":"isPayable_","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_comptrollerProxy","type":"address"}],"name":"payoutAllowed","outputs":[{"internalType":"bool","name":"payoutAllowed_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_comptrollerProxy","type":"address"},{"internalType":"address","name":"_vaultProxy","type":"address"},{"internalType":"enum IFeeManager.FeeHook","name":"","type":"uint8"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"uint256","name":"_gav","type":"uint256"}],"name":"settle","outputs":[{"internalType":"enum IFeeManager.SettlementType","name":"settlementType_","type":"uint8"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"sharesDue_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_comptrollerProxy","type":"address"},{"internalType":"address","name":"_vaultProxy","type":"address"},{"internalType":"enum IFeeManager.FeeHook","name":"_hook","type":"uint8"},{"internalType":"bytes","name":"_settlementData","type":"bytes"},{"internalType":"uint256","name":"_gav","type":"uint256"}],"name":"update","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a06040523480156200001157600080fd5b5060405162001e2338038062001e2383398101604081905262000034916200005d565b60601b6001600160601b031916608052620000b2565b8051620000578162000098565b92915050565b6000602082840312156200007057600080fd5b60006200007e84846200004a565b949350505050565b60006001600160a01b03821662000057565b620000a38162000086565b8114620000af57600080fd5b50565b60805160601c611d35620000ee6000398061026b52806103cc52806104e85280610632528061077c5280610a0852806110cf5250611d356000f3fe608060405234801561001057600080fd5b506004361061009e5760003560e01c80637998a1c4116100665780637998a1c414610129578063877fd4731461013e578063b78b48131461015e578063cbf54bb214610171578063f2d63826146101895761009e565b806305be5783146100a35780630f5f6b4f146100cc578063233faf5f146100e15780633146d058146100f457806341892d7e14610107575b600080fd5b6100b66100b13660046113bc565b61019e565b6040516100c39190611ada565b60405180910390f35b6100df6100da3660046114f2565b610260565b005b6100df6100ef366004611462565b6103c1565b6100df610102366004611432565b6104dd565b61011a610115366004611462565b610623565b6040516100c393929190611ae8565b6101316106d8565b6040516100c39190611b10565b61015161014c3660046113bc565b6106fd565b6040516100c39190611be1565b6100b661016c366004611432565b61076f565b61017961087e565b6040516100c39493929190611a99565b610191610a06565b6040516100c39190611a70565b60006101a86112e9565b506001600160a01b038216600090815260208181526040808320815160e08101835281548152600182015493810184905260028201549281018390526003820154606082015260048201546080820152600582015460a082015260069091015460c0820152929061021a904290610a2a565b905081811015610230576000935050505061025b565b600082828161023b57fe5b069050600061024a4283610a2a565b905080856060015110955050505050505b919050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146102b15760405162461bcd60e51b81526004016102a890611b61565b60405180910390fd5b6000806102c083850185611596565b91509150600082116102e45760405162461bcd60e51b81526004016102a890611ba1565b600081116103045760405162461bcd60e51b81526004016102a890611b21565b6040805160e08101825283815260208082018481526000838501818152606085018281526080860183815260a0870184815260c088018581526001600160a01b038f168087529786905294899020975188559451600188015591516002870155516003860155516004850155905160058401555160069092019190915590517f9f856f74192181b265e61298e386477299c53e0cb24be55a84416f2af4ba4a61906103b29085908590611bfd565b60405180910390a25050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104095760405162461bcd60e51b81526004016102a890611b61565b6001600160a01b038616600090815260208181526040808320600501548151601f87018490048402810184019092528582529291610469918a918a918a918a908a90819084018382808284376000920191909152508a9250610a52915050565b90508181141561047a5750506104d5565b6001600160a01b03881660008181526020819052604090819020600501839055517f040fdaa9ed9dd9e4588f1f96145d5517471e7b518fb5d56d8504eb839f50b653906104ca9085908590611bfd565b60405180910390a250505b505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105255760405162461bcd60e51b81526004016102a890611b61565b6001600160a01b0382166000818152602081905260408082209051635a0b830960e11b81529092829163b417061290610562908490600401611ada565b6040805180830381600087803b15801561057b57600080fd5b505af115801561058f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b39190611566565b91509150806105d45760405162461bcd60e51b81526004016102a890611b71565b60048301829055600583018290554260028401556040516001600160a01b038616907f67286096839b70891eb51e1532e51d6a709d69c808d1b7e24abcb24a39167d05906103b2908590611bef565b60008080336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461066f5760405162461bcd60e51b81526004016102a890611b61565b83610682575060009150819050806106cc565b600061068f8a8a87610d04565b9050806106a7576000806000935093509350506106cc565b60008113156106bf57600493506000925090506106cc565b6005935060009250820390505b96509650969350505050565b60408051808201909152600b81526a504552464f524d414e434560a81b602082015290565b6107056112e9565b506001600160a01b031660009081526020818152604091829020825160e081018452815481526001820154928101929092526002810154928201929092526003820154606082015260048201546080820152600582015460a082015260069091015460c082015290565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146107b95760405162461bcd60e51b81526004016102a890611b61565b6107c28361019e565b6107ce57506000610878565b6001600160a01b038316600090815260208190526040812042600382015560048101546005820154919290916108049083610f17565b6006840154909150801561081a57600060068501555b8282111561082a57600484018290555b866001600160a01b03167f807c2c73192f2d30961a7d01ec8dc57115a4cd9f9bd0434331beacb916c6a8cc84848460405161086793929190611c18565b60405180910390a260019450505050505b92915050565b6040805160038082526080820190925260609182916000918291906020820185803683370190505093506000846000815181106108b757fe5b602002602001019060058111156108ca57fe5b908160058111156108d757fe5b815250506001846001815181106108ea57fe5b602002602001019060058111156108fd57fe5b9081600581111561090a57fe5b8152505060058460028151811061091d57fe5b6020026020010190600581111561093057fe5b9081600581111561093d57fe5b90525060408051600380825260808201909252906020820160608036833701905050925060008360008151811061097057fe5b6020026020010190600581111561098357fe5b9081600581111561099057fe5b815250506004836001815181106109a357fe5b602002602001019060058111156109b657fe5b908160058111156109c357fe5b815250506005836002815181106109d657fe5b602002602001019060058111156109e957fe5b908160058111156109f657fe5b9052506001915081905090919293565b7f000000000000000000000000000000000000000000000000000000000000000090565b600082821115610a4c5760405162461bcd60e51b81526004016102a890611b41565b50900390565b600080866001600160a01b031663e269c3d66040518163ffffffff1660e01b815260040160206040518083038186803b158015610a8e57600080fd5b505afa158015610aa2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ac691906113da565b6001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610afe57600080fd5b505afa158015610b12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3691906115c6565b60ff16600a0a905082610b4a579050610cfb565b60008690506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015610b8a57600080fd5b505afa158015610b9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc29190611548565b90506000610c4c836001600160a01b03166370a082318b6040518263ffffffff1660e01b8152600401610bf59190611a70565b60206040518083038186803b158015610c0d57600080fd5b505afa158015610c21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c459190611548565b8390610a2a565b905080610c5f5783945050505050610cfb565b856005896005811115610c6e57fe5b1415610cdd576000610c7f89610f2d565b9150610c8d90508382610a2a565b925082610ca257859650505050505050610cfb565b6000610cb885610cb28b85610f4d565b90610f87565b9050610cc48382610a2a565b925082610cda5786975050505050505050610cfb565b50505b610cf382610cb283670de0b6b3a7640000610f4d565b955050505050505b95945050505050565b6000808390506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015610d4557600080fd5b505afa158015610d59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7d9190611548565b905080610d8f57600092505050610f10565b6040516370a0823160e01b81526000906001600160a01b038416906370a0823190610dbe908990600401611a70565b60206040518083038186803b158015610dd657600080fd5b505afa158015610dea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0e9190611548565b905081811415610e245760009350505050610f10565b6001600160a01b0387166000908152602081815260408083206006810154825160e081018452825481526001830154948101949094526002820154928401929092526003810154606084015260048101546080840152600581015460a084015260c08301829052929091610ea0908b908790879086908d610fb9565b9750905081811415610ebb5760009650505050505050610f10565b808360060181905550896001600160a01b03167f74a80d6cce2a8c3a25bdba0cd5533d238d76eb996790fb2d4544372cc7f4e35683838a604051610f0193929190611c18565b60405180910390a25050505050505b9392505050565b6000818310610f27575081610878565b50919050565b60008082806020019051810190610f4491906113f8565b91509150915091565b600082610f5c57506000610878565b82820282848281610f6957fe5b0414610f105760405162461bcd60e51b81526004016102a890611b91565b6000808211610fa85760405162461bcd60e51b81526004016102a890611b51565b818381610fb157fe5b049392505050565b60008080610fc78888610a2a565b90506000610fe182610cb287670de0b6b3a7640000610f4d565b60a087015190915080821415611000578760009450945050505061102a565b6110168383838b8b600001518c60800151611035565b94506110248b8488886110ae565b93505050505b965096945050505050565b600080611071670de0b6b3a764000061106b8a611065611055888c610f17565b61105f898e610f17565b9061117e565b906111c4565b9061122f565b9050600061108b670de0b6b3a764000061106b84886111c4565b90506110a1600061109c8884611293565b6112d9565b9998505050505050505050565b6000806110c86110be8585610a2a565b610cb28588610f4d565b90506111727f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663aa051c2c88306040518363ffffffff1660e01b815260040161111b929190611a7e565b60206040518083038186803b15801561113357600080fd5b505afa158015611147573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061116b9190611548565b829061117e565b9150505b949350505050565b60008183038183128015906111935750838113155b806111a857506000831280156111a857508381135b610f105760405162461bcd60e51b81526004016102a890611bc1565b6000826111d357506000610878565b826000191480156111e75750600160ff1b82145b156112045760405162461bcd60e51b81526004016102a890611bb1565b8282028284828161121157fe5b0514610f105760405162461bcd60e51b81526004016102a890611bb1565b60008161124e5760405162461bcd60e51b81526004016102a890611bd1565b816000191480156112625750600160ff1b83145b1561127f5760405162461bcd60e51b81526004016102a890611b81565b600082848161128a57fe5b05949350505050565b60008282018183128015906112a85750838112155b806112bd57506000831280156112bd57508381125b610f105760405162461bcd60e51b81526004016102a890611b31565b6000818312610f27575081610878565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b803561087881611cc3565b805161087881611cc3565b805161087881611cd7565b60008083601f84011261135957600080fd5b50813567ffffffffffffffff81111561137157600080fd5b60208301915083600182028301111561138957600080fd5b9250929050565b803561087881611ce0565b803561087881611ced565b805161087881611ced565b805161087881611cf6565b6000602082840312156113ce57600080fd5b60006111768484611326565b6000602082840312156113ec57600080fd5b60006111768484611331565b6000806040838503121561140b57600080fd5b60006114178585611331565b9250506020611428858286016113a6565b9150509250929050565b6000806040838503121561144557600080fd5b60006114518585611326565b925050602061142885828601611326565b60008060008060008060a0878903121561147b57600080fd5b60006114878989611326565b965050602061149889828a01611326565b95505060406114a989828a01611390565b945050606087013567ffffffffffffffff8111156114c657600080fd5b6114d289828a01611347565b935093505060806114e589828a0161139b565b9150509295509295509295565b60008060006040848603121561150757600080fd5b60006115138686611326565b935050602084013567ffffffffffffffff81111561153057600080fd5b61153c86828701611347565b92509250509250925092565b60006020828403121561155a57600080fd5b600061117684846113a6565b6000806040838503121561157957600080fd5b600061158585856113a6565b92505060206114288582860161133c565b600080604083850312156115a957600080fd5b60006115b5858561139b565b92505060206114288582860161139b565b6000602082840312156115d857600080fd5b600061117684846113b1565b60006115f08383611669565b505060200190565b61160181611c46565b82525050565b600061161282611c39565b61161c8185611c3d565b935061162783611c33565b8060005b8381101561165557815161163f88826115e4565b975061164a83611c33565b92505060010161162b565b509495945050505050565b61160181611c51565b61160181611c75565b61160181611c60565b600061168682611c39565b6116908185611c3d565b93506116a0818560208601611c80565b6116a981611cac565b9093019392505050565b60006116c0603183611c3d565b7f61646446756e6453657474696e67733a20666565506572696f64206d75737420815270062652067726561746572207468616e203607c1b602082015260400192915050565b6000611713602183611c3d565b7f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000611756601e83611c3d565b7f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815260200192915050565b600061178f601a83611c3d565b7f536166654d6174683a206469766973696f6e206279207a65726f000000000000815260200192915050565b60006117c8602583611c3d565b7f4f6e6c7920746865204665654d616e6765722063616e206d616b6520746869738152640818d85b1b60da1b602082015260400192915050565b600061180f602483611c3d565b7f6163746976617465466f7246756e643a20496e76616c696420736861726520708152637269636560e01b602082015260400192915050565b6000611855602183611c3d565b7f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000611898602183611c3d565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b60006118db602f83611c3d565b7f61646446756e6453657474696e67733a2066656552617465206d75737420626581526e02067726561746572207468616e203608c1b602082015260400192915050565b600061192c602783611c3d565b7f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f815266766572666c6f7760c81b602082015260400192915050565b6000611975602483611c3d565b7f5369676e6564536166654d6174683a207375627472616374696f6e206f766572815263666c6f7760e01b602082015260400192915050565b60006119bb602083611c3d565b7f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f815260200192915050565b805160e08301906119f88482611672565b506020820151611a0b6020850182611672565b506040820151611a1e6040850182611672565b506060820151611a316060850182611672565b506080820151611a446080850182611672565b5060a0820151611a5760a0850182611672565b5060c0820151611a6a60c0850182611672565b50505050565b6020810161087882846115f8565b60408101611a8c82856115f8565b610f1060208301846115f8565b60808082528101611aaa8187611607565b90508181036020830152611abe8186611607565b9050611acd6040830185611660565b610cfb6060830184611660565b602081016108788284611660565b60608101611af68286611669565b611b0360208301856115f8565b6111766040830184611672565b60208082528101610f10818461167b565b60208082528101610878816116b3565b6020808252810161087881611706565b6020808252810161087881611749565b6020808252810161087881611782565b60208082528101610878816117bb565b6020808252810161087881611802565b6020808252810161087881611848565b602080825281016108788161188b565b60208082528101610878816118ce565b602080825281016108788161191f565b6020808252810161087881611968565b60208082528101610878816119ae565b60e0810161087882846119e7565b602081016108788284611672565b60408101611c0b8285611672565b610f106020830184611672565b60608101611c268286611672565b611b036020830185611672565b60200190565b5190565b90815260200190565b600061087882611c63565b151590565b8061025b81611cb6565b90565b6001600160a01b031690565b60ff1690565b600061087882611c56565b60005b83811015611c9b578181015183820152602001611c83565b83811115611a6a5750506000910152565b601f01601f191690565b60068110611cc057fe5b50565b611ccc81611c46565b8114611cc057600080fd5b611ccc81611c51565b60068110611cc057600080fd5b611ccc81611c60565b611ccc81611c6f56fea264697066735822122005c5d37c70cacaaa1df4e68ce6910cc6a8bb57570d9feaf8e118dcf550005ad964736f6c634300060c00330000000000000000000000005d0a363e9a17fb839e2843ffa93c808cdafccb67
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061009e5760003560e01c80637998a1c4116100665780637998a1c414610129578063877fd4731461013e578063b78b48131461015e578063cbf54bb214610171578063f2d63826146101895761009e565b806305be5783146100a35780630f5f6b4f146100cc578063233faf5f146100e15780633146d058146100f457806341892d7e14610107575b600080fd5b6100b66100b13660046113bc565b61019e565b6040516100c39190611ada565b60405180910390f35b6100df6100da3660046114f2565b610260565b005b6100df6100ef366004611462565b6103c1565b6100df610102366004611432565b6104dd565b61011a610115366004611462565b610623565b6040516100c393929190611ae8565b6101316106d8565b6040516100c39190611b10565b61015161014c3660046113bc565b6106fd565b6040516100c39190611be1565b6100b661016c366004611432565b61076f565b61017961087e565b6040516100c39493929190611a99565b610191610a06565b6040516100c39190611a70565b60006101a86112e9565b506001600160a01b038216600090815260208181526040808320815160e08101835281548152600182015493810184905260028201549281018390526003820154606082015260048201546080820152600582015460a082015260069091015460c0820152929061021a904290610a2a565b905081811015610230576000935050505061025b565b600082828161023b57fe5b069050600061024a4283610a2a565b905080856060015110955050505050505b919050565b336001600160a01b037f0000000000000000000000005d0a363e9a17fb839e2843ffa93c808cdafccb6716146102b15760405162461bcd60e51b81526004016102a890611b61565b60405180910390fd5b6000806102c083850185611596565b91509150600082116102e45760405162461bcd60e51b81526004016102a890611ba1565b600081116103045760405162461bcd60e51b81526004016102a890611b21565b6040805160e08101825283815260208082018481526000838501818152606085018281526080860183815260a0870184815260c088018581526001600160a01b038f168087529786905294899020975188559451600188015591516002870155516003860155516004850155905160058401555160069092019190915590517f9f856f74192181b265e61298e386477299c53e0cb24be55a84416f2af4ba4a61906103b29085908590611bfd565b60405180910390a25050505050565b336001600160a01b037f0000000000000000000000005d0a363e9a17fb839e2843ffa93c808cdafccb6716146104095760405162461bcd60e51b81526004016102a890611b61565b6001600160a01b038616600090815260208181526040808320600501548151601f87018490048402810184019092528582529291610469918a918a918a918a908a90819084018382808284376000920191909152508a9250610a52915050565b90508181141561047a5750506104d5565b6001600160a01b03881660008181526020819052604090819020600501839055517f040fdaa9ed9dd9e4588f1f96145d5517471e7b518fb5d56d8504eb839f50b653906104ca9085908590611bfd565b60405180910390a250505b505050505050565b336001600160a01b037f0000000000000000000000005d0a363e9a17fb839e2843ffa93c808cdafccb6716146105255760405162461bcd60e51b81526004016102a890611b61565b6001600160a01b0382166000818152602081905260408082209051635a0b830960e11b81529092829163b417061290610562908490600401611ada565b6040805180830381600087803b15801561057b57600080fd5b505af115801561058f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b39190611566565b91509150806105d45760405162461bcd60e51b81526004016102a890611b71565b60048301829055600583018290554260028401556040516001600160a01b038616907f67286096839b70891eb51e1532e51d6a709d69c808d1b7e24abcb24a39167d05906103b2908590611bef565b60008080336001600160a01b037f0000000000000000000000005d0a363e9a17fb839e2843ffa93c808cdafccb67161461066f5760405162461bcd60e51b81526004016102a890611b61565b83610682575060009150819050806106cc565b600061068f8a8a87610d04565b9050806106a7576000806000935093509350506106cc565b60008113156106bf57600493506000925090506106cc565b6005935060009250820390505b96509650969350505050565b60408051808201909152600b81526a504552464f524d414e434560a81b602082015290565b6107056112e9565b506001600160a01b031660009081526020818152604091829020825160e081018452815481526001820154928101929092526002810154928201929092526003820154606082015260048201546080820152600582015460a082015260069091015460c082015290565b6000336001600160a01b037f0000000000000000000000005d0a363e9a17fb839e2843ffa93c808cdafccb6716146107b95760405162461bcd60e51b81526004016102a890611b61565b6107c28361019e565b6107ce57506000610878565b6001600160a01b038316600090815260208190526040812042600382015560048101546005820154919290916108049083610f17565b6006840154909150801561081a57600060068501555b8282111561082a57600484018290555b866001600160a01b03167f807c2c73192f2d30961a7d01ec8dc57115a4cd9f9bd0434331beacb916c6a8cc84848460405161086793929190611c18565b60405180910390a260019450505050505b92915050565b6040805160038082526080820190925260609182916000918291906020820185803683370190505093506000846000815181106108b757fe5b602002602001019060058111156108ca57fe5b908160058111156108d757fe5b815250506001846001815181106108ea57fe5b602002602001019060058111156108fd57fe5b9081600581111561090a57fe5b8152505060058460028151811061091d57fe5b6020026020010190600581111561093057fe5b9081600581111561093d57fe5b90525060408051600380825260808201909252906020820160608036833701905050925060008360008151811061097057fe5b6020026020010190600581111561098357fe5b9081600581111561099057fe5b815250506004836001815181106109a357fe5b602002602001019060058111156109b657fe5b908160058111156109c357fe5b815250506005836002815181106109d657fe5b602002602001019060058111156109e957fe5b908160058111156109f657fe5b9052506001915081905090919293565b7f0000000000000000000000005d0a363e9a17fb839e2843ffa93c808cdafccb6790565b600082821115610a4c5760405162461bcd60e51b81526004016102a890611b41565b50900390565b600080866001600160a01b031663e269c3d66040518163ffffffff1660e01b815260040160206040518083038186803b158015610a8e57600080fd5b505afa158015610aa2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ac691906113da565b6001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610afe57600080fd5b505afa158015610b12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3691906115c6565b60ff16600a0a905082610b4a579050610cfb565b60008690506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015610b8a57600080fd5b505afa158015610b9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc29190611548565b90506000610c4c836001600160a01b03166370a082318b6040518263ffffffff1660e01b8152600401610bf59190611a70565b60206040518083038186803b158015610c0d57600080fd5b505afa158015610c21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c459190611548565b8390610a2a565b905080610c5f5783945050505050610cfb565b856005896005811115610c6e57fe5b1415610cdd576000610c7f89610f2d565b9150610c8d90508382610a2a565b925082610ca257859650505050505050610cfb565b6000610cb885610cb28b85610f4d565b90610f87565b9050610cc48382610a2a565b925082610cda5786975050505050505050610cfb565b50505b610cf382610cb283670de0b6b3a7640000610f4d565b955050505050505b95945050505050565b6000808390506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015610d4557600080fd5b505afa158015610d59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7d9190611548565b905080610d8f57600092505050610f10565b6040516370a0823160e01b81526000906001600160a01b038416906370a0823190610dbe908990600401611a70565b60206040518083038186803b158015610dd657600080fd5b505afa158015610dea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0e9190611548565b905081811415610e245760009350505050610f10565b6001600160a01b0387166000908152602081815260408083206006810154825160e081018452825481526001830154948101949094526002820154928401929092526003810154606084015260048101546080840152600581015460a084015260c08301829052929091610ea0908b908790879086908d610fb9565b9750905081811415610ebb5760009650505050505050610f10565b808360060181905550896001600160a01b03167f74a80d6cce2a8c3a25bdba0cd5533d238d76eb996790fb2d4544372cc7f4e35683838a604051610f0193929190611c18565b60405180910390a25050505050505b9392505050565b6000818310610f27575081610878565b50919050565b60008082806020019051810190610f4491906113f8565b91509150915091565b600082610f5c57506000610878565b82820282848281610f6957fe5b0414610f105760405162461bcd60e51b81526004016102a890611b91565b6000808211610fa85760405162461bcd60e51b81526004016102a890611b51565b818381610fb157fe5b049392505050565b60008080610fc78888610a2a565b90506000610fe182610cb287670de0b6b3a7640000610f4d565b60a087015190915080821415611000578760009450945050505061102a565b6110168383838b8b600001518c60800151611035565b94506110248b8488886110ae565b93505050505b965096945050505050565b600080611071670de0b6b3a764000061106b8a611065611055888c610f17565b61105f898e610f17565b9061117e565b906111c4565b9061122f565b9050600061108b670de0b6b3a764000061106b84886111c4565b90506110a1600061109c8884611293565b6112d9565b9998505050505050505050565b6000806110c86110be8585610a2a565b610cb28588610f4d565b90506111727f0000000000000000000000005d0a363e9a17fb839e2843ffa93c808cdafccb676001600160a01b031663aa051c2c88306040518363ffffffff1660e01b815260040161111b929190611a7e565b60206040518083038186803b15801561113357600080fd5b505afa158015611147573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061116b9190611548565b829061117e565b9150505b949350505050565b60008183038183128015906111935750838113155b806111a857506000831280156111a857508381135b610f105760405162461bcd60e51b81526004016102a890611bc1565b6000826111d357506000610878565b826000191480156111e75750600160ff1b82145b156112045760405162461bcd60e51b81526004016102a890611bb1565b8282028284828161121157fe5b0514610f105760405162461bcd60e51b81526004016102a890611bb1565b60008161124e5760405162461bcd60e51b81526004016102a890611bd1565b816000191480156112625750600160ff1b83145b1561127f5760405162461bcd60e51b81526004016102a890611b81565b600082848161128a57fe5b05949350505050565b60008282018183128015906112a85750838112155b806112bd57506000831280156112bd57508381125b610f105760405162461bcd60e51b81526004016102a890611b31565b6000818312610f27575081610878565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b803561087881611cc3565b805161087881611cc3565b805161087881611cd7565b60008083601f84011261135957600080fd5b50813567ffffffffffffffff81111561137157600080fd5b60208301915083600182028301111561138957600080fd5b9250929050565b803561087881611ce0565b803561087881611ced565b805161087881611ced565b805161087881611cf6565b6000602082840312156113ce57600080fd5b60006111768484611326565b6000602082840312156113ec57600080fd5b60006111768484611331565b6000806040838503121561140b57600080fd5b60006114178585611331565b9250506020611428858286016113a6565b9150509250929050565b6000806040838503121561144557600080fd5b60006114518585611326565b925050602061142885828601611326565b60008060008060008060a0878903121561147b57600080fd5b60006114878989611326565b965050602061149889828a01611326565b95505060406114a989828a01611390565b945050606087013567ffffffffffffffff8111156114c657600080fd5b6114d289828a01611347565b935093505060806114e589828a0161139b565b9150509295509295509295565b60008060006040848603121561150757600080fd5b60006115138686611326565b935050602084013567ffffffffffffffff81111561153057600080fd5b61153c86828701611347565b92509250509250925092565b60006020828403121561155a57600080fd5b600061117684846113a6565b6000806040838503121561157957600080fd5b600061158585856113a6565b92505060206114288582860161133c565b600080604083850312156115a957600080fd5b60006115b5858561139b565b92505060206114288582860161139b565b6000602082840312156115d857600080fd5b600061117684846113b1565b60006115f08383611669565b505060200190565b61160181611c46565b82525050565b600061161282611c39565b61161c8185611c3d565b935061162783611c33565b8060005b8381101561165557815161163f88826115e4565b975061164a83611c33565b92505060010161162b565b509495945050505050565b61160181611c51565b61160181611c75565b61160181611c60565b600061168682611c39565b6116908185611c3d565b93506116a0818560208601611c80565b6116a981611cac565b9093019392505050565b60006116c0603183611c3d565b7f61646446756e6453657474696e67733a20666565506572696f64206d75737420815270062652067726561746572207468616e203607c1b602082015260400192915050565b6000611713602183611c3d565b7f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000611756601e83611c3d565b7f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815260200192915050565b600061178f601a83611c3d565b7f536166654d6174683a206469766973696f6e206279207a65726f000000000000815260200192915050565b60006117c8602583611c3d565b7f4f6e6c7920746865204665654d616e6765722063616e206d616b6520746869738152640818d85b1b60da1b602082015260400192915050565b600061180f602483611c3d565b7f6163746976617465466f7246756e643a20496e76616c696420736861726520708152637269636560e01b602082015260400192915050565b6000611855602183611c3d565b7f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000611898602183611c3d565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b60006118db602f83611c3d565b7f61646446756e6453657474696e67733a2066656552617465206d75737420626581526e02067726561746572207468616e203608c1b602082015260400192915050565b600061192c602783611c3d565b7f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f815266766572666c6f7760c81b602082015260400192915050565b6000611975602483611c3d565b7f5369676e6564536166654d6174683a207375627472616374696f6e206f766572815263666c6f7760e01b602082015260400192915050565b60006119bb602083611c3d565b7f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f815260200192915050565b805160e08301906119f88482611672565b506020820151611a0b6020850182611672565b506040820151611a1e6040850182611672565b506060820151611a316060850182611672565b506080820151611a446080850182611672565b5060a0820151611a5760a0850182611672565b5060c0820151611a6a60c0850182611672565b50505050565b6020810161087882846115f8565b60408101611a8c82856115f8565b610f1060208301846115f8565b60808082528101611aaa8187611607565b90508181036020830152611abe8186611607565b9050611acd6040830185611660565b610cfb6060830184611660565b602081016108788284611660565b60608101611af68286611669565b611b0360208301856115f8565b6111766040830184611672565b60208082528101610f10818461167b565b60208082528101610878816116b3565b6020808252810161087881611706565b6020808252810161087881611749565b6020808252810161087881611782565b60208082528101610878816117bb565b6020808252810161087881611802565b6020808252810161087881611848565b602080825281016108788161188b565b60208082528101610878816118ce565b602080825281016108788161191f565b6020808252810161087881611968565b60208082528101610878816119ae565b60e0810161087882846119e7565b602081016108788284611672565b60408101611c0b8285611672565b610f106020830184611672565b60608101611c268286611672565b611b036020830185611672565b60200190565b5190565b90815260200190565b600061087882611c63565b151590565b8061025b81611cb6565b90565b6001600160a01b031690565b60ff1690565b600061087882611c56565b60005b83811015611c9b578181015183820152602001611c83565b83811115611a6a5750506000910152565b601f01601f191690565b60068110611cc057fe5b50565b611ccc81611c46565b8114611cc057600080fd5b611ccc81611c51565b60068110611cc057600080fd5b611ccc81611c60565b611ccc81611c6f56fea264697066735822122005c5d37c70cacaaa1df4e68ce6910cc6a8bb57570d9feaf8e118dcf550005ad964736f6c634300060c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000005d0a363e9a17fb839e2843ffa93c808cdafccb67
-----Decoded View---------------
Arg [0] : _feeManager (address): 0x5D0A363E9a17fb839e2843ffa93C808CdafCCb67
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000005d0a363e9a17fb839e2843ffa93c808cdafccb67
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.