diff --git a/contracts/viewer/ProtocolViewer.sol b/contracts/viewer/ProtocolViewer.sol index 751c666..9a8330c 100644 --- a/contracts/viewer/ProtocolViewer.sol +++ b/contracts/viewer/ProtocolViewer.sol @@ -19,6 +19,7 @@ pragma experimental "ABIEncoderV2"; import { ERC20Viewer } from "./lib/ERC20Viewer.sol"; import { ManagerViewer } from "./lib/ManagerViewer.sol"; +import { OracleViewer } from "./lib/OracleViewer.sol"; import { RebalancingSetTokenViewer } from "./lib/RebalancingSetTokenViewer.sol"; import { TradingPoolViewer } from "./lib/TradingPoolViewer.sol"; import { CTokenViewer } from "./lib/CTokenViewer.sol"; @@ -35,8 +36,8 @@ import { CTokenViewer } from "./lib/CTokenViewer.sol"; /* solium-disable-next-line no-empty-blocks */ contract ProtocolViewer is ERC20Viewer, - RebalancingSetTokenViewer, TradingPoolViewer, CTokenViewer, - ManagerViewer + ManagerViewer, + OracleViewer {} diff --git a/contracts/viewer/lib/OracleViewer.sol b/contracts/viewer/lib/OracleViewer.sol new file mode 100644 index 0000000..e20e776 --- /dev/null +++ b/contracts/viewer/lib/OracleViewer.sol @@ -0,0 +1,55 @@ +/* + Copyright 2020 Set Labs Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +pragma solidity 0.5.7; +pragma experimental "ABIEncoderV2"; + +import { IOracle } from "set-protocol-oracles/contracts/meta-oracles/interfaces/IOracle.sol"; + + +/** + * @title OracleViewer + * @author Set Protocol + * + * Contract for fetching oracle state + */ +contract OracleViewer { + /* + * Fetches RebalancingSetToken liquidator for an array of RebalancingSetToken instances + * + * @param _rebalancingSetTokens[] RebalancingSetToken contract instances + * @return address[] Current liquidator being used by RebalancingSetToken + */ + function batchFetchOraclePrices( + IOracle[] calldata _oracles + ) + external + returns (uint256[] memory) + { + // Cache length of addresses to fetch states for + uint256 _addressesCount = _oracles.length; + + // Instantiate output array in memory + uint256[] memory prices = new uint256[](_addressesCount); + + // Cycles through contract addresses array and fetches the current price of each oracle + for (uint256 i = 0; i < _addressesCount; i++) { + prices[i] = _oracles[i].read(); + } + + return prices; + } +} diff --git a/contracts/viewer/lib/RebalancingSetTokenViewer.sol b/contracts/viewer/lib/RebalancingSetTokenViewer.sol index 00c1f86..1c5d8ae 100644 --- a/contracts/viewer/lib/RebalancingSetTokenViewer.sol +++ b/contracts/viewer/lib/RebalancingSetTokenViewer.sol @@ -17,7 +17,16 @@ pragma solidity 0.5.7; pragma experimental "ABIEncoderV2"; +import { ERC20Detailed } from "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol"; + +import { ILiquidator } from "set-protocol-contracts/contracts/core/interfaces/ILiquidator.sol"; +import { IPerformanceFeeCalculator } from "set-protocol-contracts/contracts/core/interfaces/IPerformanceFeeCalculator.sol"; import { IRebalancingSetToken } from "set-protocol-contracts/contracts/core/interfaces/IRebalancingSetToken.sol"; +import { IRebalancingSetTokenV2 } from "set-protocol-contracts/contracts/core/interfaces/IRebalancingSetTokenV2.sol"; +import { IRebalancingSetTokenV3 } from "set-protocol-contracts/contracts/core/interfaces/IRebalancingSetTokenV3.sol"; +import { ISetToken } from "set-protocol-contracts/contracts/core/interfaces/ISetToken.sol"; +import { ITWAPAuctionGetters } from "set-protocol-contracts/contracts/core/interfaces/ITWAPAuctionGetters.sol"; +import { PerformanceFeeLibrary } from "set-protocol-contracts/contracts/core/fee-calculators/lib/PerformanceFeeLibrary.sol"; import { RebalancingLibrary } from "set-protocol-contracts/contracts/core/lib/RebalancingLibrary.sol"; @@ -29,6 +38,69 @@ import { RebalancingLibrary } from "set-protocol-contracts/contracts/core/lib/Re */ contract RebalancingSetTokenViewer { + struct CollateralAndState { + address collateralSet; + RebalancingLibrary.State state; + } + + struct CollateralSetInfo { + address[] components; + uint256[] units; + uint256 naturalUnit; + string name; + string symbol; + } + + struct RebalancingSetRebalanceInfo { + uint256 rebalanceStartTime; + uint256 timeToPivot; + uint256 startPrice; + uint256 endPrice; + uint256 startingCurrentSets; + uint256 remainingCurrentSets; + uint256 minimumBid; + RebalancingLibrary.State rebalanceState; + ISetToken nextSet; + ILiquidator liquidator; + } + + struct RebalancingSetCreateInfo { + address manager; + address feeRecipient; + ISetToken currentSet; + ILiquidator liquidator; + uint256 unitShares; + uint256 naturalUnit; + uint256 rebalanceInterval; + uint256 entryFee; + uint256 rebalanceFee; + uint256 lastRebalanceTimestamp; + RebalancingLibrary.State rebalanceState; + string name; + string symbol; + } + + struct TWAPRebalanceInfo { + uint256 rebalanceStartTime; + uint256 timeToPivot; + uint256 startPrice; + uint256 endPrice; + uint256 startingCurrentSets; + uint256 remainingCurrentSets; + uint256 minimumBid; + RebalancingLibrary.State rebalanceState; + ISetToken nextSet; + ILiquidator liquidator; + uint256 orderSize; + uint256 orderRemaining; + uint256 totalSetsRemaining; + uint256 chunkSize; + uint256 chunkAuctionPeriod; + uint256 lastChunkAuctionEnd; + } + + /* ============ RebalancingSetV1 Functions ============ */ + /* * Fetches all RebalancingSetToken state associated with a rebalance proposal * @@ -97,6 +169,134 @@ contract RebalancingSetTokenViewer { return (rebalanceState, auctionIntegerParams); } + /* ============ Event Based Fetching Functions ============ */ + + /* + * Fetches RebalancingSetToken details. Compatible with: + * - RebalancingSetTokenV2/V3 + * - PerformanceFeeCalculator + * - Any Liquidator + * + * @param _rebalancingSetToken RebalancingSetToken contract instance + * @return RebalancingLibrary.State Current rebalance state on the RebalancingSetToken + * @return uint256[] Starting current set, start time, minimum bid, and remaining current sets + */ + function fetchNewRebalancingSetDetails( + IRebalancingSetTokenV3 _rebalancingSetToken + ) + public + view + returns ( + RebalancingSetCreateInfo memory, + PerformanceFeeLibrary.FeeState memory, + CollateralSetInfo memory, + address + ) + { + RebalancingSetCreateInfo memory rbSetInfo = getRebalancingSetInfo( + address(_rebalancingSetToken) + ); + + PerformanceFeeLibrary.FeeState memory performanceFeeInfo = getPerformanceFeeState( + address(_rebalancingSetToken) + ); + + CollateralSetInfo memory collateralSetInfo = getCollateralSetInfo( + rbSetInfo.currentSet + ); + + address performanceFeeCalculatorAddress = address(_rebalancingSetToken.rebalanceFeeCalculator()); + + return (rbSetInfo, performanceFeeInfo, collateralSetInfo, performanceFeeCalculatorAddress); + } + + /* + * Fetches all RebalancingSetToken state associated with a new rebalance auction. Compatible with: + * - RebalancingSetTokenV2/V3 + * - Any Fee Calculator + * - Any liquidator (will omit additional TWAPLiquidator state) + * + * @param _rebalancingSetToken RebalancingSetToken contract instance + * @return RebalancingLibrary.State Current rebalance state on the RebalancingSetToken + * @return uint256[] Starting current set, start time, minimum bid, and remaining current sets + */ + function fetchRBSetRebalanceDetails( + IRebalancingSetTokenV2 _rebalancingSetToken + ) + public + view + returns (RebalancingSetRebalanceInfo memory, CollateralSetInfo memory) + { + uint256[] memory auctionParams = _rebalancingSetToken.getAuctionPriceParameters(); + uint256[] memory biddingParams = _rebalancingSetToken.getBiddingParameters(); + + RebalancingSetRebalanceInfo memory rbSetInfo = RebalancingSetRebalanceInfo({ + rebalanceStartTime: auctionParams[0], + timeToPivot: auctionParams[1], + startPrice: auctionParams[2], + endPrice: auctionParams[3], + startingCurrentSets: _rebalancingSetToken.startingCurrentSetAmount(), + remainingCurrentSets: biddingParams[1], + minimumBid: biddingParams[0], + rebalanceState: _rebalancingSetToken.rebalanceState(), + nextSet: _rebalancingSetToken.nextSet(), + liquidator: _rebalancingSetToken.liquidator() + }); + + CollateralSetInfo memory collateralSetInfo = getCollateralSetInfo(_rebalancingSetToken.nextSet()); + + return (rbSetInfo, collateralSetInfo); + } + + /* + * Fetches all RebalancingSetToken state associated with a new TWAP rebalance auction. Compatible with: + * - RebalancingSetTokenV2/V3 + * - Any Fee Calculator + * - TWAPLiquidator + * + * @param _rebalancingSetToken RebalancingSetToken contract instance + * @return RebalancingLibrary.State Current rebalance state on the RebalancingSetToken + * @return uint256[] Starting current set, start time, minimum bid, and remaining current sets + */ + function fetchRBSetTWAPRebalanceDetails( + IRebalancingSetTokenV2 _rebalancingSetToken + ) + public + view + returns (TWAPRebalanceInfo memory, CollateralSetInfo memory) + { + uint256[] memory auctionParams = _rebalancingSetToken.getAuctionPriceParameters(); + uint256[] memory biddingParams = _rebalancingSetToken.getBiddingParameters(); + ILiquidator liquidator = _rebalancingSetToken.liquidator(); + + ITWAPAuctionGetters twapStateGetters = ITWAPAuctionGetters(address(liquidator)); + + TWAPRebalanceInfo memory rbSetInfo = TWAPRebalanceInfo({ + rebalanceStartTime: auctionParams[0], + timeToPivot: auctionParams[1], + startPrice: auctionParams[2], + endPrice: auctionParams[3], + startingCurrentSets: _rebalancingSetToken.startingCurrentSetAmount(), + remainingCurrentSets: biddingParams[1], + minimumBid: biddingParams[0], + rebalanceState: _rebalancingSetToken.rebalanceState(), + nextSet: _rebalancingSetToken.nextSet(), + liquidator: liquidator, + orderSize: twapStateGetters.getOrderSize(address(_rebalancingSetToken)), + orderRemaining: twapStateGetters.getOrderRemaining(address(_rebalancingSetToken)), + totalSetsRemaining: twapStateGetters.getTotalSetsRemaining(address(_rebalancingSetToken)), + chunkSize: twapStateGetters.getChunkSize(address(_rebalancingSetToken)), + chunkAuctionPeriod: twapStateGetters.getChunkAuctionPeriod(address(_rebalancingSetToken)), + lastChunkAuctionEnd: twapStateGetters.getLastChunkAuctionEnd(address(_rebalancingSetToken)) + }); + + CollateralSetInfo memory collateralSetInfo = getCollateralSetInfo(_rebalancingSetToken.nextSet()); + + return (rbSetInfo, collateralSetInfo); + } + + /* ============ Batch Fetch Functions ============ */ + /* * Fetches RebalancingSetToken states for an array of RebalancingSetToken instances * @@ -141,11 +341,121 @@ contract RebalancingSetTokenViewer { // Instantiate output array in memory uint256[] memory unitShares = new uint256[](_addressesCount); - // Cycle through contract addresses array and fetching the rebalance state of each RebalancingSet + // Cycles through contract addresses array and fetches the unitShares of each RebalancingSet for (uint256 i = 0; i < _addressesCount; i++) { unitShares[i] = _rebalancingSetTokens[i].unitShares(); } return unitShares; } + + /* + * Fetches RebalancingSetToken liquidator for an array of RebalancingSetToken instances + * + * @param _rebalancingSetTokens[] RebalancingSetToken contract instances + * @return address[] Current liquidator being used by RebalancingSetToken + */ + function batchFetchLiquidator( + IRebalancingSetTokenV2[] calldata _rebalancingSetTokens + ) + external + returns (address[] memory) + { + // Cache length of addresses to fetch states for + uint256 _addressesCount = _rebalancingSetTokens.length; + + // Instantiate output array in memory + address[] memory liquidators = new address[](_addressesCount); + + // Cycles through contract addresses array and fetches the liquidator addresss of each RebalancingSet + for (uint256 i = 0; i < _addressesCount; i++) { + liquidators[i] = address(_rebalancingSetTokens[i].liquidator()); + } + + return liquidators; + } + + /* + * Fetches RebalancingSetToken state and current collateral for an array of RebalancingSetToken instances + * + * @param _rebalancingSetTokens[] RebalancingSetToken contract instances + * @return CollateralAndState[] Current collateral and state of RebalancingSetTokens + */ + function batchFetchStateAndCollateral( + IRebalancingSetToken[] calldata _rebalancingSetTokens + ) + external + returns (CollateralAndState[] memory) + { + // Cache length of addresses to fetch states for + uint256 _addressesCount = _rebalancingSetTokens.length; + + // Instantiate output array in memory + CollateralAndState[] memory statuses = new CollateralAndState[](_addressesCount); + + // Cycles through contract addresses array and fetches the liquidator addresss of each RebalancingSet + for (uint256 i = 0; i < _addressesCount; i++) { + statuses[i].collateralSet = address(_rebalancingSetTokens[i].currentSet()); + statuses[i].state = _rebalancingSetTokens[i].rebalanceState(); + } + + return statuses; + } + + /* ============ Internal Functions ============ */ + + function getCollateralSetInfo( + ISetToken _collateralSet + ) + internal + view + returns (CollateralSetInfo memory) + { + return CollateralSetInfo({ + components: _collateralSet.getComponents(), + units: _collateralSet.getUnits(), + naturalUnit: _collateralSet.naturalUnit(), + name: ERC20Detailed(address(_collateralSet)).name(), + symbol: ERC20Detailed(address(_collateralSet)).symbol() + }); + } + + function getRebalancingSetInfo( + address _rebalancingSetToken + ) + internal + view + returns (RebalancingSetCreateInfo memory) + { + IRebalancingSetTokenV2 rebalancingSetTokenV2Instance = IRebalancingSetTokenV2(_rebalancingSetToken); + + return RebalancingSetCreateInfo({ + manager: rebalancingSetTokenV2Instance.manager(), + feeRecipient: rebalancingSetTokenV2Instance.feeRecipient(), + currentSet: rebalancingSetTokenV2Instance.currentSet(), + liquidator: rebalancingSetTokenV2Instance.liquidator(), + unitShares: rebalancingSetTokenV2Instance.unitShares(), + naturalUnit: rebalancingSetTokenV2Instance.naturalUnit(), + rebalanceInterval: rebalancingSetTokenV2Instance.rebalanceInterval(), + entryFee: rebalancingSetTokenV2Instance.entryFee(), + rebalanceFee: rebalancingSetTokenV2Instance.rebalanceFee(), + lastRebalanceTimestamp: rebalancingSetTokenV2Instance.lastRebalanceTimestamp(), + rebalanceState: rebalancingSetTokenV2Instance.rebalanceState(), + name: rebalancingSetTokenV2Instance.name(), + symbol: rebalancingSetTokenV2Instance.symbol() + }); + } + + function getPerformanceFeeState( + address _rebalancingSetToken + ) + internal + view + returns (PerformanceFeeLibrary.FeeState memory) + { + IRebalancingSetTokenV2 rebalancingSetTokenV3Instance = IRebalancingSetTokenV2(_rebalancingSetToken); + + address rebalanceFeeCalculatorAddress = address(rebalancingSetTokenV3Instance.rebalanceFeeCalculator()); + return IPerformanceFeeCalculator(rebalanceFeeCalculatorAddress).feeState(_rebalancingSetToken); + } } diff --git a/contracts/viewer/lib/TradingPoolViewer.sol b/contracts/viewer/lib/TradingPoolViewer.sol index d2192f2..e89ebfb 100644 --- a/contracts/viewer/lib/TradingPoolViewer.sol +++ b/contracts/viewer/lib/TradingPoolViewer.sol @@ -17,20 +17,15 @@ pragma solidity 0.5.7; pragma experimental "ABIEncoderV2"; -import { ERC20Detailed } from "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol"; - import { ISocialTradingManager } from "set-protocol-strategies/contracts/managers/interfaces/ISocialTradingManager.sol"; import { SocialTradingLibrary } from "set-protocol-strategies/contracts/managers/lib/SocialTradingLibrary.sol"; -import { IFeeCalculator } from "set-protocol-contracts/contracts/core/interfaces/IFeeCalculator.sol"; -import { ILiquidator } from "set-protocol-contracts/contracts/core/interfaces/ILiquidator.sol"; import { IPerformanceFeeCalculator } from "set-protocol-contracts/contracts/core/interfaces/IPerformanceFeeCalculator.sol"; import { IRebalancingSetTokenV2 } from "set-protocol-contracts/contracts/core/interfaces/IRebalancingSetTokenV2.sol"; import { IRebalancingSetTokenV3 } from "set-protocol-contracts/contracts/core/interfaces/IRebalancingSetTokenV3.sol"; -import { ITWAPAuctionGetters } from "set-protocol-contracts/contracts/core/interfaces/ITWAPAuctionGetters.sol"; -import { ISetToken } from "set-protocol-contracts/contracts/core/interfaces/ISetToken.sol"; import { PerformanceFeeLibrary } from "set-protocol-contracts/contracts/core/fee-calculators/lib/PerformanceFeeLibrary.sol"; -import { RebalancingLibrary } from "set-protocol-contracts/contracts/core/lib/RebalancingLibrary.sol"; + +import { RebalancingSetTokenViewer } from "./RebalancingSetTokenViewer.sol"; /** @@ -40,71 +35,26 @@ import { RebalancingLibrary } from "set-protocol-contracts/contracts/core/lib/Re * Interfaces for fetching multiple TradingPool state in a single read. Includes state * specific to managing pool as well as underlying RebalancingSetTokenV2 state. */ -contract TradingPoolViewer { - - struct TradingPoolCreateInfo { - address manager; - address feeRecipient; - ISetToken currentSet; - uint256 unitShares; - uint256 naturalUnit; - uint256 rebalanceInterval; - uint256 entryFee; - uint256 rebalanceFee; - uint256 lastRebalanceTimestamp; - RebalancingLibrary.State rebalanceState; - string name; - string symbol; - } - - struct TradingPoolRebalanceInfo { - uint256 rebalanceStartTime; - uint256 timeToPivot; - uint256 startPrice; - uint256 endPrice; - uint256 startingCurrentSets; - uint256 remainingCurrentSets; - uint256 minimumBid; - RebalancingLibrary.State rebalanceState; - ISetToken nextSet; - ILiquidator liquidator; - } - - struct TradingPoolTWAPRebalanceInfo { - uint256 rebalanceStartTime; - uint256 timeToPivot; - uint256 startPrice; - uint256 endPrice; - uint256 startingCurrentSets; - uint256 remainingCurrentSets; - uint256 minimumBid; - RebalancingLibrary.State rebalanceState; - ISetToken nextSet; - ILiquidator liquidator; - uint256 orderSize; - uint256 orderRemaining; - uint256 totalSetsRemaining; - uint256 chunkSize; - uint256 chunkAuctionPeriod; - uint256 lastChunkAuctionEnd; - } - - struct CollateralSetInfo { - address[] components; - uint256[] units; - uint256 naturalUnit; - string name; - string symbol; - } - +contract TradingPoolViewer is RebalancingSetTokenViewer { + + /* + * Fetches TradingPool details. Compatible with: + * - RebalancingSetTokenV2/V3 + * - Any Fee Calculator + * - Any Liquidator + * + * @param _rebalancingSetToken RebalancingSetToken contract instance + * @return RebalancingLibrary.State Current rebalance state on the RebalancingSetToken + * @return uint256[] Starting current set, start time, minimum bid, and remaining current sets + */ function fetchNewTradingPoolDetails( IRebalancingSetTokenV2 _tradingPool ) external view - returns (SocialTradingLibrary.PoolInfo memory, TradingPoolCreateInfo memory, CollateralSetInfo memory) + returns (SocialTradingLibrary.PoolInfo memory, RebalancingSetCreateInfo memory, CollateralSetInfo memory) { - TradingPoolCreateInfo memory tradingPoolInfo = getTradingPoolInfo( + RebalancingSetCreateInfo memory tradingPoolInfo = getRebalancingSetInfo( address(_tradingPool) ); @@ -119,6 +69,16 @@ contract TradingPoolViewer { return (poolInfo, tradingPoolInfo, collateralSetInfo); } + /* + * Fetches TradingPoolV2 details. Compatible with: + * - RebalancingSetTokenV2/V3 + * - PerformanceFeeCalculator + * - Any Liquidator + * + * @param _rebalancingSetToken RebalancingSetToken contract instance + * @return RebalancingLibrary.State Current rebalance state on the RebalancingSetToken + * @return uint256[] Starting current set, start time, minimum bid, and remaining current sets + */ function fetchNewTradingPoolV2Details( IRebalancingSetTokenV3 _tradingPool ) @@ -126,55 +86,47 @@ contract TradingPoolViewer { view returns ( SocialTradingLibrary.PoolInfo memory, - TradingPoolCreateInfo memory, + RebalancingSetCreateInfo memory, PerformanceFeeLibrary.FeeState memory, CollateralSetInfo memory, address ) { - TradingPoolCreateInfo memory tradingPoolInfo = getTradingPoolInfo( - address(_tradingPool) - ); + ( + RebalancingSetCreateInfo memory tradingPoolInfo, + PerformanceFeeLibrary.FeeState memory performanceFeeInfo, + CollateralSetInfo memory collateralSetInfo, + address performanceFeeCalculatorAddress + ) = fetchNewRebalancingSetDetails(_tradingPool); SocialTradingLibrary.PoolInfo memory poolInfo = ISocialTradingManager(tradingPoolInfo.manager).pools( address(_tradingPool) ); - PerformanceFeeLibrary.FeeState memory performanceFeeInfo = getPerformanceFeeState( - address(_tradingPool) - ); - - CollateralSetInfo memory collateralSetInfo = getCollateralSetInfo( - tradingPoolInfo.currentSet - ); - - address performanceFeeCalculatorAddress = address(_tradingPool.rebalanceFeeCalculator()); - return (poolInfo, tradingPoolInfo, performanceFeeInfo, collateralSetInfo, performanceFeeCalculatorAddress); } + /* + * Fetches all TradingPool state associated with a new rebalance auction. Compatible with: + * - RebalancingSetTokenV2/V3 + * - Any Fee Calculator + * - Any liquidator (will omit additional TWAPLiquidator state) + * + * @param _rebalancingSetToken RebalancingSetToken contract instance + * @return RebalancingLibrary.State Current rebalance state on the RebalancingSetToken + * @return uint256[] Starting current set, start time, minimum bid, and remaining current sets + */ function fetchTradingPoolRebalanceDetails( IRebalancingSetTokenV2 _tradingPool ) external view - returns (SocialTradingLibrary.PoolInfo memory, TradingPoolRebalanceInfo memory, CollateralSetInfo memory) + returns (SocialTradingLibrary.PoolInfo memory, RebalancingSetRebalanceInfo memory, CollateralSetInfo memory) { - uint256[] memory auctionParams = _tradingPool.getAuctionPriceParameters(); - uint256[] memory biddingParams = _tradingPool.getBiddingParameters(); - - TradingPoolRebalanceInfo memory tradingPoolInfo = TradingPoolRebalanceInfo({ - rebalanceStartTime: auctionParams[0], - timeToPivot: auctionParams[1], - startPrice: auctionParams[2], - endPrice: auctionParams[3], - startingCurrentSets: _tradingPool.startingCurrentSetAmount(), - remainingCurrentSets: biddingParams[1], - minimumBid: biddingParams[0], - rebalanceState: _tradingPool.rebalanceState(), - nextSet: _tradingPool.nextSet(), - liquidator: _tradingPool.liquidator() - }); + ( + RebalancingSetRebalanceInfo memory tradingPoolInfo, + CollateralSetInfo memory collateralSetInfo + ) = fetchRBSetRebalanceDetails(_tradingPool); address manager = _tradingPool.manager(); @@ -182,57 +134,28 @@ contract TradingPoolViewer { address(_tradingPool) ); - CollateralSetInfo memory collateralSetInfo = getCollateralSetInfo(_tradingPool.nextSet()); - return (poolInfo, tradingPoolInfo, collateralSetInfo); } - function fetchRBSetTWAPRebalanceDetails( - IRebalancingSetTokenV2 _rebalancingSetToken - ) - public - view - returns (TradingPoolTWAPRebalanceInfo memory, CollateralSetInfo memory) - { - uint256[] memory auctionParams = _rebalancingSetToken.getAuctionPriceParameters(); - uint256[] memory biddingParams = _rebalancingSetToken.getBiddingParameters(); - ILiquidator liquidator = _rebalancingSetToken.liquidator(); - - ITWAPAuctionGetters twapStateGetters = ITWAPAuctionGetters(address(liquidator)); - - TradingPoolTWAPRebalanceInfo memory tradingPoolInfo = TradingPoolTWAPRebalanceInfo({ - rebalanceStartTime: auctionParams[0], - timeToPivot: auctionParams[1], - startPrice: auctionParams[2], - endPrice: auctionParams[3], - startingCurrentSets: _rebalancingSetToken.startingCurrentSetAmount(), - remainingCurrentSets: biddingParams[1], - minimumBid: biddingParams[0], - rebalanceState: _rebalancingSetToken.rebalanceState(), - nextSet: _rebalancingSetToken.nextSet(), - liquidator: liquidator, - orderSize: twapStateGetters.getOrderSize(address(_rebalancingSetToken)), - orderRemaining: twapStateGetters.getOrderRemaining(address(_rebalancingSetToken)), - totalSetsRemaining: twapStateGetters.getTotalSetsRemaining(address(_rebalancingSetToken)), - chunkSize: twapStateGetters.getChunkSize(address(_rebalancingSetToken)), - chunkAuctionPeriod: twapStateGetters.getChunkAuctionPeriod(address(_rebalancingSetToken)), - lastChunkAuctionEnd: twapStateGetters.getLastChunkAuctionEnd(address(_rebalancingSetToken)) - }); - - CollateralSetInfo memory collateralSetInfo = getCollateralSetInfo(_rebalancingSetToken.nextSet()); - - return (tradingPoolInfo, collateralSetInfo); - } - + /* + * Fetches all TradingPool state associated with a new TWAP rebalance auction. Compatible with: + * - RebalancingSetTokenV2/V3 + * - Any Fee Calculator + * - TWAP Liquidator + * + * @param _rebalancingSetToken RebalancingSetToken contract instance + * @return RebalancingLibrary.State Current rebalance state on the RebalancingSetToken + * @return uint256[] Starting current set, start time, minimum bid, and remaining current sets + */ function fetchTradingPoolTWAPRebalanceDetails( IRebalancingSetTokenV2 _tradingPool ) external view - returns (SocialTradingLibrary.PoolInfo memory, TradingPoolTWAPRebalanceInfo memory, CollateralSetInfo memory) + returns (SocialTradingLibrary.PoolInfo memory, TWAPRebalanceInfo memory, CollateralSetInfo memory) { ( - TradingPoolTWAPRebalanceInfo memory tradingPoolInfo, + TWAPRebalanceInfo memory tradingPoolInfo, CollateralSetInfo memory collateralSetInfo ) = fetchRBSetTWAPRebalanceDetails(_tradingPool); @@ -361,60 +284,4 @@ contract TradingPoolViewer { return feeStates; } - - /* ============ Internal Functions ============ */ - - function getCollateralSetInfo( - ISetToken _collateralSet - ) - internal - view - returns (CollateralSetInfo memory) - { - return CollateralSetInfo({ - components: _collateralSet.getComponents(), - units: _collateralSet.getUnits(), - naturalUnit: _collateralSet.naturalUnit(), - name: ERC20Detailed(address(_collateralSet)).name(), - symbol: ERC20Detailed(address(_collateralSet)).symbol() - }); - } - - function getTradingPoolInfo( - address _tradingPool - ) - internal - view - returns (TradingPoolCreateInfo memory) - { - IRebalancingSetTokenV2 rebalancingSetTokenV2Instance = IRebalancingSetTokenV2(_tradingPool); - - return TradingPoolCreateInfo({ - manager: rebalancingSetTokenV2Instance.manager(), - feeRecipient: rebalancingSetTokenV2Instance.feeRecipient(), - currentSet: rebalancingSetTokenV2Instance.currentSet(), - unitShares: rebalancingSetTokenV2Instance.unitShares(), - naturalUnit: rebalancingSetTokenV2Instance.naturalUnit(), - rebalanceInterval: rebalancingSetTokenV2Instance.rebalanceInterval(), - entryFee: rebalancingSetTokenV2Instance.entryFee(), - rebalanceFee: rebalancingSetTokenV2Instance.rebalanceFee(), - lastRebalanceTimestamp: rebalancingSetTokenV2Instance.lastRebalanceTimestamp(), - rebalanceState: rebalancingSetTokenV2Instance.rebalanceState(), - name: rebalancingSetTokenV2Instance.name(), - symbol: rebalancingSetTokenV2Instance.symbol() - }); - } - - function getPerformanceFeeState( - address _tradingPool - ) - internal - view - returns (PerformanceFeeLibrary.FeeState memory) - { - IRebalancingSetTokenV3 rebalancingSetTokenV3Instance = IRebalancingSetTokenV3(_tradingPool); - - address rebalanceFeeCalculatorAddress = address(rebalancingSetTokenV3Instance.rebalanceFeeCalculator()); - return IPerformanceFeeCalculator(rebalanceFeeCalculatorAddress).feeState(_tradingPool); - } } diff --git a/package.json b/package.json index 94d4b7b..debd023 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "set-protocol-viewers", - "version": "1.0.12", + "version": "1.0.13", "main": "dist/artifacts/index.js", "typings": "dist/typings/artifacts/index.d.ts", "files": [ diff --git a/test/contracts/viewer/lib/oracleViewer.spec.ts b/test/contracts/viewer/lib/oracleViewer.spec.ts new file mode 100644 index 0000000..b632e58 --- /dev/null +++ b/test/contracts/viewer/lib/oracleViewer.spec.ts @@ -0,0 +1,94 @@ +require('module-alias/register'); + +import * as chai from 'chai'; +import { Address } from 'set-protocol-utils'; +import { BigNumber } from 'bignumber.js'; + +import ChaiSetup from '@utils/chaiSetup'; +import { BigNumberSetup } from '@utils/bigNumberSetup'; +import { + OracleViewerContract, +} from '@utils/contracts'; + +import { + Blockchain +} from 'set-protocol-contracts'; +import { + OracleHelper, + UpdatableOracleMockContract +} from 'set-protocol-oracles'; +import { ProtocolViewerHelper } from '@utils/helpers/protocolViewerHelper'; +import { ether } from '@utils/units'; + +BigNumberSetup.configure(); +ChaiSetup.configure(); +const { expect } = chai; +const blockchain = new Blockchain(web3); + +contract('OracleViewer', accounts => { + const [ + deployerAccount, + ] = accounts; + + let wrappedETHOracle: UpdatableOracleMockContract; + let wrappedBTCOracle: UpdatableOracleMockContract; + let usdcOracle: UpdatableOracleMockContract; + let daiOracle: UpdatableOracleMockContract; + + let wrappedETHPrice: BigNumber; + let wrappedBTCPrice: BigNumber; + let usdcPrice: BigNumber; + let daiPrice: BigNumber; + + + const oracleHelper = new OracleHelper(deployerAccount); + const protocolViewerHelper = new ProtocolViewerHelper(deployerAccount); + + let oracleViewer: OracleViewerContract; + + beforeEach(async () => { + await blockchain.saveSnapshotAsync(); + + wrappedETHPrice = ether(128); + wrappedBTCPrice = ether(7500); + usdcPrice = ether(1); + daiPrice = ether(1); + + wrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedETHPrice); + wrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedBTCPrice); + usdcOracle = await oracleHelper.deployUpdatableOracleMockAsync(usdcPrice); + daiOracle = await oracleHelper.deployUpdatableOracleMockAsync(daiPrice); + + oracleViewer = await protocolViewerHelper.deployOracleViewerAsync(); + }); + + afterEach(async () => { + await blockchain.revertAsync(); + }); + + describe('#batchFetchOraclePrices', async () => { + let subjectOracleAddresses: Address[]; + + beforeEach(async () => { + subjectOracleAddresses = [ + wrappedETHOracle.address, + wrappedBTCOracle.address, + usdcOracle.address, + daiOracle.address, + ]; + }); + + async function subject(): Promise { + return oracleViewer.batchFetchOraclePrices.callAsync( + subjectOracleAddresses, + ); + } + + it('fetches oracle prices', async () => { + const oraclePrices = await subject(); + + const expectedOraclePrices = [wrappedETHPrice, wrappedBTCPrice, usdcPrice, daiPrice]; + expect(JSON.stringify(oraclePrices)).to.equal(JSON.stringify(expectedOraclePrices)); + }); + }); +}); \ No newline at end of file diff --git a/test/contracts/viewer/lib/rebalancingSetTokenViewer.spec.ts b/test/contracts/viewer/lib/rebalancingSetTokenViewer.spec.ts index 91d42e6..1e832df 100644 --- a/test/contracts/viewer/lib/rebalancingSetTokenViewer.spec.ts +++ b/test/contracts/viewer/lib/rebalancingSetTokenViewer.spec.ts @@ -11,25 +11,38 @@ import { BigNumberSetup } from '@utils/bigNumberSetup'; import { ConstantAuctionPriceCurveContract, CoreMockContract, + LinearAuctionLiquidatorContract, + OracleWhiteListContract, + PerformanceFeeCalculatorContract, RebalancingSetTokenContract, RebalancingSetTokenFactoryContract, + RebalancingSetTokenV3Contract, + RebalancingSetTokenV3FactoryContract, SetTokenContract, SetTokenFactoryContract, + StandardTokenMockContract, TransferProxyContract, + TWAPLiquidatorContract, VaultContract, WhiteListContract, } from 'set-protocol-contracts'; +import { + UpdatableOracleMockContract, +} from 'set-protocol-oracles'; import { RebalancingSetTokenViewerContract, } from '@utils/contracts'; import { ether } from '@utils/units'; import { ONE_DAY_IN_SECONDS, + ONE_YEAR_IN_SECONDS, DEFAULT_AUCTION_PRICE_NUMERATOR, DEFAULT_AUCTION_PRICE_DIVISOR, DEFAULT_REBALANCE_TIME_TO_PIVOT, DEFAULT_REBALANCE_START_PRICE, + DEFAULT_REBALANCING_NATURAL_UNIT, ZERO, + ONE_HOUR_IN_SECONDS, } from '@utils/constants'; import { expectRevertError } from '@utils/tokenAssertions'; @@ -37,8 +50,15 @@ import { Blockchain, CoreHelper, ERC20Helper, + FeeCalculatorHelper, + LiquidatorHelper, RebalancingHelper, + RebalancingSetV3Helper, + ValuationHelper, } from 'set-protocol-contracts'; +import { + OracleHelper +} from 'set-protocol-oracles'; import { ProtocolViewerHelper } from '@utils/helpers/protocolViewerHelper'; const CoreMock = @@ -69,13 +89,23 @@ contract('RebalancingSetTokenViewer', accounts => { const coreHelper = new CoreHelper(deployerAccount, deployerAccount); const erc20Helper = new ERC20Helper(deployerAccount); - const protocolViewerHelper = new ProtocolViewerHelper(deployerAccount); + const feeCalculatorHelper = new FeeCalculatorHelper(deployerAccount); + const oracleHelper = new OracleHelper(deployerAccount); const rebalancingHelper = new RebalancingHelper( deployerAccount, coreHelper, erc20Helper, blockchain ); + const rebalancingSetV3Helper = new RebalancingSetV3Helper( + deployerAccount, + coreHelper, + erc20Helper, + blockchain + ); + const valuationHelper = new ValuationHelper(deployerAccount, coreHelper, erc20Helper, oracleHelper); + const liquidatorHelper = new LiquidatorHelper(deployerAccount, erc20Helper, valuationHelper); + const viewerHelper = new ProtocolViewerHelper(deployerAccount); let rebalancingSetTokenViewer: RebalancingSetTokenViewerContract; @@ -109,7 +139,7 @@ contract('RebalancingSetTokenViewer', accounts => { await coreHelper.addFactoryAsync(coreMock, rebalancingFactory); await rebalancingHelper.addPriceLibraryAsync(coreMock, constantAuctionPriceCurve); - rebalancingSetTokenViewer = await protocolViewerHelper.deployRebalancingSetTokenViewerAsync(); + rebalancingSetTokenViewer = await viewerHelper.deployRebalancingSetTokenViewerAsync(); }); afterEach(async () => { @@ -376,6 +406,512 @@ contract('RebalancingSetTokenViewer', accounts => { }); }); + describe('RebalancingSetTokenV2/V3', async () => { + let rebalancingSetTokenV3Factory: RebalancingSetTokenV3FactoryContract; + let rebalancingSetTokenV3: RebalancingSetTokenV3Contract; + + let oracleWhiteList: OracleWhiteListContract; + + let wrappedETH: StandardTokenMockContract; + let wrappedBTC: StandardTokenMockContract; + let usdc: StandardTokenMockContract; + let dai: StandardTokenMockContract; + + let set1: SetTokenContract; + let set2: SetTokenContract; + + let collateralComponents: Address[]; + let set1Units: BigNumber[]; + let set1NaturalUnit: BigNumber; + + let set2Units: BigNumber[]; + let set2NaturalUnit: BigNumber; + + let firstSetUnits: BigNumber; + let lastRebalanceTimestamp: BigNumber; + + let wrappedETHOracle: UpdatableOracleMockContract; + let wrappedBTCOracle: UpdatableOracleMockContract; + let usdcOracle: UpdatableOracleMockContract; + let daiOracle: UpdatableOracleMockContract; + + let wrappedETHPrice: BigNumber; + let wrappedBTCPrice: BigNumber; + let usdcPrice: BigNumber; + let daiPrice: BigNumber; + + let liquidatorWhitelist: WhiteListContract; + let feeCalculatorWhitelist: WhiteListContract; + let performanceFeeCalculator: PerformanceFeeCalculatorContract; + let liquidator: LinearAuctionLiquidatorContract; + let twapLiquidator: TWAPLiquidatorContract; + + beforeEach(async () => { + // Oracles + wrappedETH = await erc20Helper.deployTokenAsync(deployerAccount, 18); + await rebalancingComponentWhiteList.addAddress.sendTransactionAsync(wrappedETH.address); + wrappedBTC = await erc20Helper.deployTokenAsync(deployerAccount, 8); + await rebalancingComponentWhiteList.addAddress.sendTransactionAsync(wrappedBTC.address); + usdc = await erc20Helper.deployTokenAsync(deployerAccount, 6); + await rebalancingComponentWhiteList.addAddress.sendTransactionAsync(usdc.address); + dai = await erc20Helper.deployTokenAsync(deployerAccount, 18); + await rebalancingComponentWhiteList.addAddress.sendTransactionAsync(dai.address); + + await erc20Helper.approveTransfersAsync([wrappedBTC, wrappedETH, dai, usdc], transferProxy.address); + + wrappedETHPrice = ether(128); + wrappedBTCPrice = ether(7500); + usdcPrice = ether(1); + daiPrice = ether(1); + + wrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedETHPrice); + wrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedBTCPrice); + usdcOracle = await oracleHelper.deployUpdatableOracleMockAsync(usdcPrice); + daiOracle = await oracleHelper.deployUpdatableOracleMockAsync(daiPrice); + + oracleWhiteList = await coreHelper.deployOracleWhiteListAsync( + [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], + [wrappedETHOracle.address, wrappedBTCOracle.address, usdcOracle.address, daiOracle.address], + ); + + // Liquidators + const auctionPeriod = ONE_DAY_IN_SECONDS; + const rangeStart = new BigNumber(1); // 1% above fair value + const rangeEnd = new BigNumber(23); // 23% below fair value + liquidator = await liquidatorHelper.deployLinearAuctionLiquidatorAsync( + coreMock.address, + oracleWhiteList.address, + auctionPeriod, + rangeStart, + rangeEnd, + 'LinearLiquidator', + ); + const assetPairHashes = [ + liquidatorHelper.generateAssetPairHashes(wrappedETH.address, wrappedBTC.address), + ]; + const assetPairBounds = [ + {min: ether(10 ** 4).toString(), max: ether(10 ** 6).toString()}, + ]; + twapLiquidator = await viewerHelper.deployTWAPLiquidatorAsync( + coreMock.address, + oracleWhiteList.address, + auctionPeriod, + rangeStart, + rangeEnd, + assetPairHashes, + assetPairBounds, + 'TWAPLiquidator', + ); + liquidatorWhitelist = await coreHelper.deployWhiteListAsync([liquidator.address, twapLiquidator.address]); + + // Fee Calculators + const maxProfitFeePercentage = ether(.5); + const maxStreamingFeePercentage = ether(.1); + performanceFeeCalculator = await feeCalculatorHelper.deployPerformanceFeeCalculatorAsync( + coreMock.address, + oracleWhiteList.address, + maxProfitFeePercentage, + maxStreamingFeePercentage + ); + feeCalculatorWhitelist = await coreHelper.deployWhiteListAsync([performanceFeeCalculator.address]); + + // RebalancingSetTokenV3Factory + rebalancingSetTokenV3Factory = await viewerHelper.deployRebalancingSetTokenV3FactoryAsync( + coreMock.address, + rebalancingComponentWhiteList.address, + liquidatorWhitelist.address, + feeCalculatorWhitelist.address, + ); + await coreHelper.addFactoryAsync(coreMock, rebalancingSetTokenV3Factory); + + // Collateral Sets + collateralComponents = [wrappedETH.address, wrappedBTC.address]; + set1Units = [wrappedBTCPrice.div(wrappedETHPrice).mul(10 ** 12), new BigNumber(100)]; + set1NaturalUnit = new BigNumber(10 ** 12); + set1 = await coreHelper.createSetTokenAsync( + coreMock, + factory.address, + collateralComponents, + set1Units, + set1NaturalUnit, + ); + + set2Units = [wrappedBTCPrice.div(wrappedETHPrice).mul(10 ** 12), new BigNumber(300)]; + set2NaturalUnit = new BigNumber(10 ** 12); + set2 = await coreHelper.createSetTokenAsync( + coreMock, + factory.address, + collateralComponents, + set2Units, + set2NaturalUnit, + ); + + // Deploy RebalancingSetTokenV3 + const failPeriod = ONE_DAY_IN_SECONDS; + const { timestamp } = await web3.eth.getBlock('latest'); + lastRebalanceTimestamp = new BigNumber(timestamp).sub(ONE_DAY_IN_SECONDS); + const calculatorData = feeCalculatorHelper.generatePerformanceFeeCallDataBuffer( + ONE_DAY_IN_SECONDS.mul(30), + ONE_YEAR_IN_SECONDS, + ether(.2), + ether(.02) + ); + + const firstNaturalUnit = DEFAULT_REBALANCING_NATURAL_UNIT; + const firstSetValue = await valuationHelper.calculateSetTokenValueAsync(set1, oracleWhiteList); + firstSetUnits = new BigNumber(100).mul(firstNaturalUnit).mul(10 ** 18).div(firstSetValue).round(0, 3); + const firstSetCallData = rebalancingSetV3Helper.generateRebalancingSetTokenV3CallData( + managerAccount, + twapLiquidator.address, + managerAccount, + performanceFeeCalculator.address, + ONE_DAY_IN_SECONDS, + failPeriod, + lastRebalanceTimestamp, + ZERO, + calculatorData + ); + + rebalancingSetTokenV3 = await rebalancingSetV3Helper.createRebalancingTokenV3Async( + coreMock, + rebalancingSetTokenV3Factory.address, + [set1.address], + [firstSetUnits], + firstNaturalUnit, + firstSetCallData + ); + }); + + describe('#fetchNewRebalancingSetDetails', async () => { + let subjectRebalancingSet: Address; + + beforeEach(async () => { + subjectRebalancingSet = rebalancingSetTokenV3.address; + }); + + async function subject(): Promise { + return rebalancingSetTokenViewer.fetchNewRebalancingSetDetails.callAsync( + subjectRebalancingSet + ); + } + + it('fetches the correct RebalancingSetTokenV3/TradingPool data', async () => { + const [ tradingPoolInfo, , , ] = await subject(); + + expect(tradingPoolInfo.manager).to.equal(managerAccount); + expect(tradingPoolInfo.feeRecipient).to.equal(managerAccount); + expect(tradingPoolInfo.currentSet).to.equal(set1.address); + expect(tradingPoolInfo.liquidator).to.equal(twapLiquidator.address); + expect(tradingPoolInfo.name).to.equal('Rebalancing Set Token'); + expect(tradingPoolInfo.symbol).to.equal('RBSET'); + expect(tradingPoolInfo.unitShares).to.be.bignumber.equal(firstSetUnits); + expect(tradingPoolInfo.naturalUnit).to.be.bignumber.equal(DEFAULT_REBALANCING_NATURAL_UNIT); + expect(tradingPoolInfo.rebalanceInterval).to.be.bignumber.equal(ONE_DAY_IN_SECONDS); + expect(tradingPoolInfo.entryFee).to.be.bignumber.equal(ZERO); + expect(tradingPoolInfo.lastRebalanceTimestamp).to.be.bignumber.equal(lastRebalanceTimestamp); + expect(tradingPoolInfo.rebalanceState).to.be.bignumber.equal(ZERO); + }); + + it('fetches the correct RebalancingSetTokenV3/Performance Fee data', async () => { + const [ , performanceFeeState, , ] = await subject(); + const [ + profitFeePeriod, + highWatermarkResetPeriod, + profitFeePercentage, + streamingFeePercentage, + highWatermark, + lastProfitFeeTimestamp, + lastStreamingFeeTimestamp, + ] = performanceFeeState; + + const expectedFeeStates: any = await performanceFeeCalculator.feeState.callAsync(rebalancingSetTokenV3.address); + + expect(profitFeePeriod).to.equal(expectedFeeStates.profitFeePeriod); + expect(highWatermarkResetPeriod).to.equal(expectedFeeStates.highWatermarkResetPeriod); + expect(profitFeePercentage).to.equal(expectedFeeStates.profitFeePercentage); + expect(streamingFeePercentage).to.equal(expectedFeeStates.streamingFeePercentage); + expect(highWatermark).to.equal(expectedFeeStates.highWatermark); + expect(lastProfitFeeTimestamp).to.equal(expectedFeeStates.lastProfitFeeTimestamp); + expect(lastStreamingFeeTimestamp).to.equal(expectedFeeStates.lastStreamingFeeTimestamp); + }); + + it('fetches the correct CollateralSet data', async () => { + const [ , , collateralSetData, ] = await subject(); + + expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(collateralComponents)); + expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(set1Units)); + expect(collateralSetData.naturalUnit).to.be.bignumber.equal(set1NaturalUnit); + expect(collateralSetData.name).to.equal('Set Token'); + expect(collateralSetData.symbol).to.equal('SET'); + }); + + it('fetches the correct PerformanceFeeCalculator address', async () => { + const [ , , , performanceFeeCalculatorAddress ] = await subject(); + + expect(performanceFeeCalculatorAddress).to.equal(performanceFeeCalculator.address); + }); + }); + + describe('#fetchRBSetTWAPRebalanceDetails', async () => { + let subjectRebalancingSet: Address; + + let currentSetToken: SetTokenContract; + let nextSet: SetTokenContract; + + beforeEach(async () => { + currentSetToken = set1; + nextSet = set2; + + // Issue currentSetToken + await coreMock.issue.sendTransactionAsync( + currentSetToken.address, + ether(8), + {from: deployerAccount} + ); + await erc20Helper.approveTransfersAsync([currentSetToken], transferProxy.address); + + // Use issued currentSetToken to issue rebalancingSetToken + const rebalancingSetQuantityToIssue = ether(7); + await coreMock.issue.sendTransactionAsync(rebalancingSetTokenV3.address, rebalancingSetQuantityToIssue); + + await blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS); + + const liquidatorData = liquidatorHelper.generateTWAPLiquidatorCalldata( + ether(10 ** 5), + ONE_HOUR_IN_SECONDS, + ); + + await rebalancingSetTokenV3.startRebalance.sendTransactionAsync( + nextSet.address, + liquidatorData, + { from: managerAccount } + ); + + subjectRebalancingSet = rebalancingSetTokenV3.address; + }); + + async function subject(): Promise { + return rebalancingSetTokenViewer.fetchRBSetTWAPRebalanceDetails.callAsync( + subjectRebalancingSet + ); + } + + it('fetches the correct RebalancingSetTokenV2/TradingPool data', async () => { + const [ rbSetData, ] = await subject(); + + const auctionPriceParams = await rebalancingSetTokenV3.getAuctionPriceParameters.callAsync(); + const startingCurrentSets = await rebalancingSetTokenV3.startingCurrentSetAmount.callAsync(); + const biddingParams = await rebalancingSetTokenV3.getBiddingParameters.callAsync(); + + expect(rbSetData.rebalanceStartTime).to.be.bignumber.equal(auctionPriceParams[0]); + expect(rbSetData.timeToPivot).to.be.bignumber.equal(auctionPriceParams[1]); + expect(rbSetData.startPrice).to.be.bignumber.equal(auctionPriceParams[2]); + expect(rbSetData.endPrice).to.be.bignumber.equal(auctionPriceParams[3]); + expect(rbSetData.startingCurrentSets).to.be.bignumber.equal(startingCurrentSets); + expect(rbSetData.remainingCurrentSets).to.be.bignumber.equal(biddingParams[1]); + expect(rbSetData.minimumBid).to.be.bignumber.equal(biddingParams[0]); + expect(rbSetData.rebalanceState).to.be.bignumber.equal(new BigNumber(2)); + expect(rbSetData.nextSet).to.equal(nextSet.address); + expect(rbSetData.liquidator).to.equal(twapLiquidator.address); + }); + + it('fetches the correct CollateralSet data', async () => { + const [ , collateralSetData ] = await subject(); + + expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(collateralComponents)); + expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(set2Units)); + expect(collateralSetData.naturalUnit).to.be.bignumber.equal(set2NaturalUnit); + expect(collateralSetData.name).to.equal('Set Token'); + expect(collateralSetData.symbol).to.equal('SET'); + }); + }); + + describe('#fetchRBSetRebalanceDetails', async () => { + let subjectRebalancingSet: Address; + + let currentSetToken: SetTokenContract; + let nextSet: SetTokenContract; + + beforeEach(async () => { + currentSetToken = set1; + nextSet = set2; + + // Issue currentSetToken + await coreMock.issue.sendTransactionAsync( + currentSetToken.address, + ether(8), + {from: deployerAccount} + ); + await erc20Helper.approveTransfersAsync([currentSetToken], transferProxy.address); + + // Use issued currentSetToken to issue rebalancingSetToken + const rebalancingSetQuantityToIssue = ether(7); + await coreMock.issue.sendTransactionAsync(rebalancingSetTokenV3.address, rebalancingSetQuantityToIssue); + + await blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS); + + const liquidatorData = liquidatorHelper.generateTWAPLiquidatorCalldata( + ether(10 ** 5), + ONE_HOUR_IN_SECONDS, + ); + + await rebalancingSetTokenV3.setLiquidator.sendTransactionAsync(liquidator.address, { from: managerAccount }); + + await rebalancingSetTokenV3.startRebalance.sendTransactionAsync( + nextSet.address, + liquidatorData, + { from: managerAccount } + ); + + subjectRebalancingSet = rebalancingSetTokenV3.address; + }); + + async function subject(): Promise { + return rebalancingSetTokenViewer.fetchRBSetRebalanceDetails.callAsync( + subjectRebalancingSet + ); + } + + it('fetches the correct RebalancingSetTokenV2/TradingPool data', async () => { + const [ rbSetData, ] = await subject(); + + const auctionPriceParams = await rebalancingSetTokenV3.getAuctionPriceParameters.callAsync(); + const startingCurrentSets = await rebalancingSetTokenV3.startingCurrentSetAmount.callAsync(); + const biddingParams = await rebalancingSetTokenV3.getBiddingParameters.callAsync(); + + expect(rbSetData.rebalanceStartTime).to.be.bignumber.equal(auctionPriceParams[0]); + expect(rbSetData.timeToPivot).to.be.bignumber.equal(auctionPriceParams[1]); + expect(rbSetData.startPrice).to.be.bignumber.equal(auctionPriceParams[2]); + expect(rbSetData.endPrice).to.be.bignumber.equal(auctionPriceParams[3]); + expect(rbSetData.startingCurrentSets).to.be.bignumber.equal(startingCurrentSets); + expect(rbSetData.remainingCurrentSets).to.be.bignumber.equal(biddingParams[1]); + expect(rbSetData.minimumBid).to.be.bignumber.equal(biddingParams[0]); + expect(rbSetData.rebalanceState).to.be.bignumber.equal(new BigNumber(2)); + expect(rbSetData.nextSet).to.equal(nextSet.address); + expect(rbSetData.liquidator).to.equal(liquidator.address); + }); + + it('fetches the correct CollateralSet data', async () => { + const [ , collateralSetData ] = await subject(); + + expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(collateralComponents)); + expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(set2Units)); + expect(collateralSetData.naturalUnit).to.be.bignumber.equal(set2NaturalUnit); + expect(collateralSetData.name).to.equal('Set Token'); + expect(collateralSetData.symbol).to.equal('SET'); + }); + }); + + describe('Batch Fetching Functions', async () => { + let otherRebalancingSetV3: RebalancingSetTokenV3Contract; + + beforeEach(async () => { + const failPeriod = ONE_DAY_IN_SECONDS; + const { timestamp } = await web3.eth.getBlock('latest'); + lastRebalanceTimestamp = new BigNumber(timestamp).sub(ONE_DAY_IN_SECONDS); + const calculatorData = feeCalculatorHelper.generatePerformanceFeeCallDataBuffer( + ONE_DAY_IN_SECONDS.mul(30), + ONE_YEAR_IN_SECONDS, + ether(.2), + ether(.02) + ); + + const firstNaturalUnit = DEFAULT_REBALANCING_NATURAL_UNIT; + const firstSetValue = await valuationHelper.calculateSetTokenValueAsync(set1, oracleWhiteList); + firstSetUnits = new BigNumber(100).mul(firstNaturalUnit).mul(10 ** 18).div(firstSetValue).round(0, 3); + const firstSetCallData = rebalancingSetV3Helper.generateRebalancingSetTokenV3CallData( + managerAccount, + liquidator.address, + managerAccount, + performanceFeeCalculator.address, + ONE_DAY_IN_SECONDS, + failPeriod, + lastRebalanceTimestamp, + ZERO, + calculatorData + ); + + otherRebalancingSetV3 = await rebalancingSetV3Helper.createRebalancingTokenV3Async( + coreMock, + rebalancingSetTokenV3Factory.address, + [set2.address], + [firstSetUnits], + firstNaturalUnit, + firstSetCallData + ); + }); + + describe('#batchFetchLiquidator', async () => { + let subjectRebalancingSets: Address[]; + + beforeEach(async () => { + subjectRebalancingSets = [rebalancingSetTokenV3.address, otherRebalancingSetV3.address]; + }); + + async function subject(): Promise { + return rebalancingSetTokenViewer.batchFetchLiquidator.callAsync( + subjectRebalancingSets + ); + } + + it('fetches the correct liquidator array', async () => { + const liquidators = await subject(); + + const expectedLiquidators = [twapLiquidator.address, liquidator.address]; + expect(JSON.stringify(liquidators)).to.equal(JSON.stringify(expectedLiquidators)); + }); + }); + + describe('#batchFetchStateAndCollateral', async () => { + let subjectRebalancingSets: Address[]; + + beforeEach(async () => { + // Issue currentSetToken + await coreMock.issue.sendTransactionAsync( + set1.address, + ether(8), + {from: deployerAccount} + ); + await erc20Helper.approveTransfersAsync([set1], transferProxy.address); + + // Use issued currentSetToken to issue rebalancingSetToken + const rebalancingSetQuantityToIssue = ether(7); + await coreMock.issue.sendTransactionAsync(rebalancingSetTokenV3.address, rebalancingSetQuantityToIssue); + + await blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS); + + const liquidatorData = liquidatorHelper.generateTWAPLiquidatorCalldata( + ether(10 ** 5), + ONE_HOUR_IN_SECONDS, + ); + + await rebalancingSetTokenV3.startRebalance.sendTransactionAsync( + set2.address, + liquidatorData, + { from: managerAccount } + ); + + subjectRebalancingSets = [rebalancingSetTokenV3.address, otherRebalancingSetV3.address]; + }); + + async function subject(): Promise { + return rebalancingSetTokenViewer.batchFetchStateAndCollateral.callAsync( + subjectRebalancingSets + ); + } + + it('fetches the correct liquidator array', async () => { + const statuses: any[] = await subject(); + + expect(statuses[0].collateralSet).to.equal(set1.address); + expect(statuses[1].collateralSet).to.equal(set2.address); + expect(statuses[0].state).to.be.bignumber.equal(new BigNumber(2)); + expect(statuses[1].state).to.be.bignumber.equal(ZERO); + }); + }); + }); + }); + describe('#batchFetchRebalanceStateAsync', async () => { let subjectRebalancingSetAddresses: Address[]; diff --git a/test/contracts/viewer/lib/tradingPoolViewer.spec.ts b/test/contracts/viewer/lib/tradingPoolViewer.spec.ts index 7631122..31d8804 100644 --- a/test/contracts/viewer/lib/tradingPoolViewer.spec.ts +++ b/test/contracts/viewer/lib/tradingPoolViewer.spec.ts @@ -329,6 +329,7 @@ contract('TradingPoolViewer', accounts => { expect(rbSetData.manager).to.equal(setManager.address); expect(rbSetData.feeRecipient).to.equal(feeRecipient); expect(rbSetData.currentSet).to.equal(currentSetToken.address); + expect(rbSetData.liquidator).to.equal(liquidator.address); expect(rbSetData.name).to.equal('Rebalancing Set Token'); expect(rbSetData.symbol).to.equal('RBSET'); expect(rbSetData.unitShares).to.be.bignumber.equal(DEFAULT_UNIT_SHARES); @@ -518,6 +519,7 @@ contract('TradingPoolViewer', accounts => { expect(tradingPoolInfo.manager).to.equal(setManager.address); expect(tradingPoolInfo.feeRecipient).to.equal(feeRecipient); expect(tradingPoolInfo.currentSet).to.equal(collateralSet.address); + expect(tradingPoolInfo.liquidator).to.equal(liquidator.address); expect(tradingPoolInfo.name).to.equal('Rebalancing Set Token'); expect(tradingPoolInfo.symbol).to.equal('RBSET'); expect(tradingPoolInfo.unitShares).to.be.bignumber.equal(firstSetUnits); @@ -538,7 +540,6 @@ contract('TradingPoolViewer', accounts => { expect(poolInfo.feeUpdateTimestamp).to.be.bignumber.equal(ZERO); }); - it('fetches the correct RebalancingSetTokenV3/Performance Fee data', async () => { const [ , , performanceFeeState, , ] = await subject(); const [ @@ -696,100 +697,6 @@ contract('TradingPoolViewer', accounts => { }); }); - describe('#fetchRBSetTWAPRebalanceDetails', async () => { - let subjectTradingPool: Address; - - let nextSet: SetTokenContract; - - beforeEach(async () => { - rebalancingFactory = await coreHelper.deployRebalancingSetTokenV3FactoryAsync( - coreMock.address, - rebalancingComponentWhiteList.address, - liquidatorWhitelist.address, - feeCalculatorWhitelist.address, - ); - await coreHelper.addFactoryAsync(coreMock, rebalancingFactory); - - const currentSetToken = set1; - - const failPeriod = ONE_DAY_IN_SECONDS; - const { timestamp } = await web3.eth.getBlock('latest'); - const lastRebalanceTimestamp = timestamp; - rebalancingSetToken = await rebalancingSetV3Helper.createDefaultRebalancingSetTokenV3Async( - coreMock, - rebalancingFactory.address, - deployerAccount, - twapLiquidator.address, - feeRecipient, - fixedFeeCalculator.address, - currentSetToken.address, - failPeriod, - lastRebalanceTimestamp, - ); - - // Issue currentSetToken - await coreMock.issue.sendTransactionAsync( - currentSetToken.address, - ether(8), - {from: deployerAccount} - ); - await erc20Helper.approveTransfersAsync([currentSetToken], transferProxy.address); - - // Use issued currentSetToken to issue rebalancingSetToken - const rebalancingSetQuantityToIssue = ether(7); - await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, rebalancingSetQuantityToIssue); - - await blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS); - - const liquidatorData = liquidatorHelper.generateTWAPLiquidatorCalldata( - ether(10 ** 6), - ONE_DAY_IN_SECONDS, - ); - nextSet = set2; - await rebalancingSetToken.startRebalance.sendTransactionAsync( - nextSet.address, - liquidatorData - ); - - subjectTradingPool = rebalancingSetToken.address; - }); - - async function subject(): Promise { - return poolViewer.fetchRBSetTWAPRebalanceDetails.callAsync( - subjectTradingPool - ); - } - - it('fetches the correct RebalancingSetTokenV2/TradingPool data', async () => { - const [ rbSetData, ] = await subject(); - - const auctionPriceParams = await rebalancingSetToken.getAuctionPriceParameters.callAsync(); - const startingCurrentSets = await rebalancingSetToken.startingCurrentSetAmount.callAsync(); - const biddingParams = await rebalancingSetToken.getBiddingParameters.callAsync(); - - expect(rbSetData.rebalanceStartTime).to.be.bignumber.equal(auctionPriceParams[0]); - expect(rbSetData.timeToPivot).to.be.bignumber.equal(auctionPriceParams[1]); - expect(rbSetData.startPrice).to.be.bignumber.equal(auctionPriceParams[2]); - expect(rbSetData.endPrice).to.be.bignumber.equal(auctionPriceParams[3]); - expect(rbSetData.startingCurrentSets).to.be.bignumber.equal(startingCurrentSets); - expect(rbSetData.remainingCurrentSets).to.be.bignumber.equal(biddingParams[1]); - expect(rbSetData.minimumBid).to.be.bignumber.equal(biddingParams[0]); - expect(rbSetData.rebalanceState).to.be.bignumber.equal(new BigNumber(2)); - expect(rbSetData.nextSet).to.equal(nextSet.address); - expect(rbSetData.liquidator).to.equal(twapLiquidator.address); - }); - - it('fetches the correct CollateralSet data', async () => { - const [ , collateralSetData ] = await subject(); - - expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(set2Components)); - expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(set2Units)); - expect(collateralSetData.naturalUnit).to.be.bignumber.equal(set2NaturalUnit); - expect(collateralSetData.name).to.equal('Set Token'); - expect(collateralSetData.symbol).to.equal('SET'); - }); - }); - describe('#fetchTradingPoolTWAPRebalanceDetails', async () => { let subjectTradingPool: Address; let setManager: SocialTradingManagerMockContract; diff --git a/test/contracts/viewer/protocolViewer.spec.ts b/test/contracts/viewer/protocolViewer.spec.ts index cd9f348..f44eb15 100644 --- a/test/contracts/viewer/protocolViewer.spec.ts +++ b/test/contracts/viewer/protocolViewer.spec.ts @@ -448,6 +448,7 @@ contract('ProtocolViewer', accounts => { let currentSetToken: SetTokenContract; let nextSet: SetTokenContract; let currentAllocation: BigNumber; + let newAllocation: BigNumber; let lastRebalanceTimestamp: BigNumber; let entryFee: BigNumber; let rebalanceFee: BigNumber; @@ -598,7 +599,351 @@ contract('ProtocolViewer', accounts => { ); }); - describe('#fetchRBSetTWAPRebalanceDetails', async () => { + describe('#fetchNewTradingPoolDetails', async () => { + let subjectTradingPool: Address; + + let newFee: BigNumber; + let feeUpdateTimestamp: BigNumber; + beforeEach(async () => { + newFee = ether(.04); + await setManager.updateFee.sendTransactionAsync( + rebalancingSetToken.address, + newFee + ); + + const { timestamp } = await web3.eth.getBlock('latest'); + feeUpdateTimestamp = new BigNumber(timestamp); + + subjectTradingPool = rebalancingSetToken.address; + }); + + async function subject(): Promise { + return protocolViewer.fetchNewTradingPoolDetails.callAsync( + subjectTradingPool + ); + } + + it('fetches the correct poolInfo data', async () => { + const [ poolInfo, , ] = await subject(); + + expect(poolInfo.trader).to.equal(trader); + expect(poolInfo.allocator).to.equal(allocator); + expect(poolInfo.currentAllocation).to.be.bignumber.equal(currentAllocation); + expect(poolInfo.newEntryFee).to.be.bignumber.equal(newFee); + expect(poolInfo.feeUpdateTimestamp).to.be.bignumber.equal(feeUpdateTimestamp); + }); + + it('fetches the correct TradingPool data', async () => { + const [ , rbSetData, ] = await subject(); + + expect(rbSetData.manager).to.equal(setManager.address); + expect(rbSetData.feeRecipient).to.equal(feeRecipient); + expect(rbSetData.currentSet).to.equal(currentSetToken.address); + expect(rbSetData.name).to.equal('Rebalancing Set Token'); + expect(rbSetData.symbol).to.equal('RBSET'); + expect(rbSetData.unitShares).to.be.bignumber.equal(DEFAULT_UNIT_SHARES); + expect(rbSetData.naturalUnit).to.be.bignumber.equal(DEFAULT_REBALANCING_NATURAL_UNIT); + expect(rbSetData.rebalanceInterval).to.be.bignumber.equal(ONE_DAY_IN_SECONDS); + expect(rbSetData.entryFee).to.be.bignumber.equal(entryFee); + expect(rbSetData.rebalanceFee).to.be.bignumber.equal(rebalanceFee); + expect(rbSetData.lastRebalanceTimestamp).to.be.bignumber.equal(lastRebalanceTimestamp); + expect(rbSetData.rebalanceState).to.be.bignumber.equal(ZERO); + }); + + it('fetches the correct CollateralSet data', async () => { + const [ , , collateralSetData ] = await subject(); + + expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(set1Components)); + expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(set1Units)); + expect(collateralSetData.naturalUnit).to.be.bignumber.equal(set1NaturalUnit); + expect(collateralSetData.name).to.equal('Set Token'); + expect(collateralSetData.symbol).to.equal('SET'); + }); + }); + + describe('#fetchNewTradingPoolV2Details and fetchNewRebalancingSetDetails', async () => { + let subjectTradingPool: Address; + + let ethOracleWhiteList: OracleWhiteListContract; + let usdOracleWhiteList: OracleWhiteListContract; + + let wrappedETH: StandardTokenMockContract; + let wrappedBTC: StandardTokenMockContract; + let usdc: StandardTokenMockContract; + let dai: StandardTokenMockContract; + + let collateralSet: SetTokenContract; + let collateralSetComponents: Address[]; + let collateralSetUnits: BigNumber[]; + let collateralSetNaturalUnit: BigNumber; + + let firstSetUnits: BigNumber; + let currentAllocation: BigNumber; + + let usdWrappedETHOracle: UpdatableOracleMockContract; + let usdWrappedBTCOracle: UpdatableOracleMockContract; + let usdUSDCOracle: UpdatableOracleMockContract; + let usdDaiOracle: UpdatableOracleMockContract; + + let ethWrappedETHOracle: UpdatableOracleMockContract; + let ethWrappedBTCOracle: UpdatableOracleMockContract; + let ethUSDCOracle: UpdatableOracleMockContract; + let ethDaiOracle: UpdatableOracleMockContract; + + let ethPerformanceFeeCalculator: PerformanceFeeCalculatorContract; + + beforeEach(async () => { + wrappedETH = await erc20Helper.deployTokenAsync(deployerAccount, 18); + wrappedBTC = await erc20Helper.deployTokenAsync(deployerAccount, 8); + usdc = await erc20Helper.deployTokenAsync(deployerAccount, 6); + dai = await erc20Helper.deployTokenAsync(deployerAccount, 18); + + let wrappedETHPrice: BigNumber; + let wrappedBTCPrice: BigNumber; + let usdcPrice: BigNumber; + let daiPrice: BigNumber; + + wrappedETHPrice = ether(128); + wrappedBTCPrice = ether(7500); + usdcPrice = ether(1); + daiPrice = ether(1); + + usdWrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedETHPrice); + usdWrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedBTCPrice); + usdUSDCOracle = await oracleHelper.deployUpdatableOracleMockAsync(usdcPrice); + usdDaiOracle = await oracleHelper.deployUpdatableOracleMockAsync(daiPrice); + + usdOracleWhiteList = await coreHelper.deployOracleWhiteListAsync( + [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], + [usdWrappedETHOracle.address, usdWrappedBTCOracle.address, usdUSDCOracle.address, usdDaiOracle.address], + ); + + ethWrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync( + wrappedETHPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) + ); + ethWrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync( + wrappedBTCPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) + ); + ethUSDCOracle = await oracleHelper.deployUpdatableOracleMockAsync( + usdcPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) + ); + ethDaiOracle = await oracleHelper.deployUpdatableOracleMockAsync( + daiPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) + ); + + ethOracleWhiteList = await coreHelper.deployOracleWhiteListAsync( + [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], + [ethWrappedETHOracle.address, ethWrappedBTCOracle.address, ethUSDCOracle.address, ethDaiOracle.address], + ); + + const maxProfitFeePercentage = ether(.5); + const maxStreamingFeePercentage = ether(.1); + ethPerformanceFeeCalculator = await feeCalculatorHelper.deployPerformanceFeeCalculatorAsync( + coreMock.address, + ethOracleWhiteList.address, + maxProfitFeePercentage, + maxStreamingFeePercentage + ); + await coreHelper.addAddressToWhiteList(ethPerformanceFeeCalculator.address, feeCalculatorWhitelist); + + collateralSetComponents = [wrappedETH.address, wrappedBTC.address]; + collateralSetUnits = [wrappedBTCPrice.div(wrappedETHPrice).mul(10 ** 12), new BigNumber(100)]; + collateralSetNaturalUnit = new BigNumber(10 ** 12); + collateralSet = await coreHelper.createSetTokenAsync( + coreMock, + factory.address, + collateralSetComponents, + collateralSetUnits, + collateralSetNaturalUnit, + ); + + setManager = await viewerHelper.deploySocialTradingManagerMockAsync(); + + const failPeriod = ONE_DAY_IN_SECONDS; + const { timestamp } = await web3.eth.getBlock('latest'); + lastRebalanceTimestamp = new BigNumber(timestamp); + + const calculatorData = feeCalculatorHelper.generatePerformanceFeeCallDataBuffer( + ONE_DAY_IN_SECONDS.mul(30), + ONE_YEAR_IN_SECONDS, + ether(.2), + ether(.02) + ); + + const firstNaturalUnit = DEFAULT_REBALANCING_NATURAL_UNIT; + const firstSetValue = await valuationHelper.calculateSetTokenValueAsync(collateralSet, usdOracleWhiteList); + firstSetUnits = new BigNumber(100).mul(firstNaturalUnit).mul(10 ** 18).div(firstSetValue).round(0, 3); + const firstSetCallData = rebalancingSetV3Helper.generateRebalancingSetTokenV3CallData( + setManager.address, + liquidator.address, + feeRecipient, + ethPerformanceFeeCalculator.address, + ONE_DAY_IN_SECONDS, + failPeriod, + lastRebalanceTimestamp, + ZERO, + calculatorData + ); + + rebalancingSetToken = await rebalancingSetV3Helper.createRebalancingTokenV3Async( + coreMock, + rebalancingFactoryV3.address, + [collateralSet.address], + [firstSetUnits], + firstNaturalUnit, + firstSetCallData + ); + + currentAllocation = ether(.6); + await setManager.updateRecord.sendTransactionAsync( + rebalancingSetToken.address, + trader, + allocator, + currentAllocation + ); + + subjectTradingPool = rebalancingSetToken.address; + }); + + async function subject(): Promise { + return protocolViewer.fetchNewTradingPoolV2Details.callAsync( + subjectTradingPool + ); + } + + async function subjectRBSet(): Promise { + return protocolViewer.fetchNewRebalancingSetDetails.callAsync( + subjectTradingPool + ); + } + + it('fetches the correct RebalancingSetTokenV3/TradingPool data', async () => { + const [ , tradingPoolInfo, , , ] = await subject(); + + expect(tradingPoolInfo.manager).to.equal(setManager.address); + expect(tradingPoolInfo.feeRecipient).to.equal(feeRecipient); + expect(tradingPoolInfo.currentSet).to.equal(collateralSet.address); + expect(tradingPoolInfo.liquidator).to.equal(liquidator.address); + expect(tradingPoolInfo.name).to.equal('Rebalancing Set Token'); + expect(tradingPoolInfo.symbol).to.equal('RBSET'); + expect(tradingPoolInfo.unitShares).to.be.bignumber.equal(firstSetUnits); + expect(tradingPoolInfo.naturalUnit).to.be.bignumber.equal(DEFAULT_REBALANCING_NATURAL_UNIT); + expect(tradingPoolInfo.rebalanceInterval).to.be.bignumber.equal(ONE_DAY_IN_SECONDS); + expect(tradingPoolInfo.entryFee).to.be.bignumber.equal(ZERO); + expect(tradingPoolInfo.lastRebalanceTimestamp).to.be.bignumber.equal(lastRebalanceTimestamp); + expect(tradingPoolInfo.rebalanceState).to.be.bignumber.equal(ZERO); + }); + + it('fetches the correct poolInfo data', async () => { + const [ poolInfo, , , , ] = await subject(); + + expect(poolInfo.trader).to.equal(trader); + expect(poolInfo.allocator).to.equal(allocator); + expect(poolInfo.currentAllocation).to.be.bignumber.equal(currentAllocation); + expect(poolInfo.newEntryFee).to.be.bignumber.equal(ZERO); + expect(poolInfo.feeUpdateTimestamp).to.be.bignumber.equal(ZERO); + }); + + + it('fetches the correct RebalancingSetTokenV3/Performance Fee data', async () => { + const [ , , performanceFeeState, , ] = await subject(); + const [ + profitFeePeriod, + highWatermarkResetPeriod, + profitFeePercentage, + streamingFeePercentage, + highWatermark, + lastProfitFeeTimestamp, + lastStreamingFeeTimestamp, + ] = performanceFeeState; + + const expectedFeeStates: any = + await ethPerformanceFeeCalculator.feeState.callAsync(rebalancingSetToken.address); + + expect(profitFeePeriod).to.equal(expectedFeeStates.profitFeePeriod); + expect(highWatermarkResetPeriod).to.equal(expectedFeeStates.highWatermarkResetPeriod); + expect(profitFeePercentage).to.equal(expectedFeeStates.profitFeePercentage); + expect(streamingFeePercentage).to.equal(expectedFeeStates.streamingFeePercentage); + expect(highWatermark).to.equal(expectedFeeStates.highWatermark); + expect(lastProfitFeeTimestamp).to.equal(expectedFeeStates.lastProfitFeeTimestamp); + expect(lastStreamingFeeTimestamp).to.equal(expectedFeeStates.lastStreamingFeeTimestamp); + }); + + it('fetches the correct CollateralSet data', async () => { + const [ , , , collateralSetData, ] = await subject(); + + expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(collateralSetComponents)); + expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(collateralSetUnits)); + expect(collateralSetData.naturalUnit).to.be.bignumber.equal(collateralSetNaturalUnit); + expect(collateralSetData.name).to.equal('Set Token'); + expect(collateralSetData.symbol).to.equal('SET'); + }); + + it('fetches the correct PerformanceFeeCalculator address', async () => { + const [ , , , , performanceFeeCalculatorAddress ] = await subject(); + + expect(performanceFeeCalculatorAddress).to.equal(ethPerformanceFeeCalculator.address); + }); + + it('fetches the correct RebalancingSetTokenV3 data', async () => { + const [ tradingPoolInfo, , , ] = await subjectRBSet(); + + expect(tradingPoolInfo.manager).to.equal(setManager.address); + expect(tradingPoolInfo.feeRecipient).to.equal(feeRecipient); + expect(tradingPoolInfo.currentSet).to.equal(collateralSet.address); + expect(tradingPoolInfo.liquidator).to.equal(liquidator.address); + expect(tradingPoolInfo.name).to.equal('Rebalancing Set Token'); + expect(tradingPoolInfo.symbol).to.equal('RBSET'); + expect(tradingPoolInfo.unitShares).to.be.bignumber.equal(firstSetUnits); + expect(tradingPoolInfo.naturalUnit).to.be.bignumber.equal(DEFAULT_REBALANCING_NATURAL_UNIT); + expect(tradingPoolInfo.rebalanceInterval).to.be.bignumber.equal(ONE_DAY_IN_SECONDS); + expect(tradingPoolInfo.entryFee).to.be.bignumber.equal(ZERO); + expect(tradingPoolInfo.lastRebalanceTimestamp).to.be.bignumber.equal(lastRebalanceTimestamp); + expect(tradingPoolInfo.rebalanceState).to.be.bignumber.equal(ZERO); + }); + + it('fetches the correct RebalancingSetTokenV3/Performance Fee data', async () => { + const [ , performanceFeeState, , ] = await subjectRBSet(); + const [ + profitFeePeriod, + highWatermarkResetPeriod, + profitFeePercentage, + streamingFeePercentage, + highWatermark, + lastProfitFeeTimestamp, + lastStreamingFeeTimestamp, + ] = performanceFeeState; + + const expectedFeeStates: any = + await ethPerformanceFeeCalculator.feeState.callAsync(rebalancingSetToken.address); + + expect(profitFeePeriod).to.equal(expectedFeeStates.profitFeePeriod); + expect(highWatermarkResetPeriod).to.equal(expectedFeeStates.highWatermarkResetPeriod); + expect(profitFeePercentage).to.equal(expectedFeeStates.profitFeePercentage); + expect(streamingFeePercentage).to.equal(expectedFeeStates.streamingFeePercentage); + expect(highWatermark).to.equal(expectedFeeStates.highWatermark); + expect(lastProfitFeeTimestamp).to.equal(expectedFeeStates.lastProfitFeeTimestamp); + expect(lastStreamingFeeTimestamp).to.equal(expectedFeeStates.lastStreamingFeeTimestamp); + }); + + it('fetches the correct CollateralSet data', async () => { + const [ , , collateralSetData, ] = await subjectRBSet(); + + expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(collateralSetComponents)); + expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(collateralSetUnits)); + expect(collateralSetData.naturalUnit).to.be.bignumber.equal(collateralSetNaturalUnit); + expect(collateralSetData.name).to.equal('Set Token'); + expect(collateralSetData.symbol).to.equal('SET'); + }); + + it('fetches the correct PerformanceFeeCalculator address', async () => { + const [ , , , performanceFeeCalculatorAddress ] = await subjectRBSet(); + + expect(performanceFeeCalculatorAddress).to.equal(ethPerformanceFeeCalculator.address); + }); + }); + + describe('#fetchTradingPoolRebalanceDetails and fetchRBSetRebalanceDetails', async () => { let subjectTradingPool: Address; beforeEach(async () => { @@ -612,35 +957,52 @@ contract('ProtocolViewer', accounts => { // Use issued currentSetToken to issue rebalancingSetToken const rebalancingSetQuantityToIssue = ether(7); - await coreMock.issue.sendTransactionAsync(rebalancingSetTokenV3.address, rebalancingSetQuantityToIssue); + await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, rebalancingSetQuantityToIssue); await blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS); - const liquidatorData = liquidatorHelper.generateTWAPLiquidatorCalldata( - ether(10 ** 6), - ONE_DAY_IN_SECONDS, - ); - await rebalancingSetTokenV3.startRebalance.sendTransactionAsync( + const liquidatorData = '0x'; + nextSet = set2; + newAllocation = ether(.4); + await setManager.rebalance.sendTransactionAsync( + rebalancingSetToken.address, nextSet.address, + newAllocation, liquidatorData ); - subjectTradingPool = rebalancingSetTokenV3.address; + subjectTradingPool = rebalancingSetToken.address; }); - async function subject(): Promise { - return protocolViewer.fetchRBSetTWAPRebalanceDetails.callAsync( + async function subjectSocialTrading(): Promise { + return protocolViewer.fetchTradingPoolRebalanceDetails.callAsync( subjectTradingPool ); } - it('fetches the correct RebalancingSetTokenV3 data', async () => { - const [ rbSetData, ] = await subject(); + async function subjectRBSet(): Promise { + return protocolViewer.fetchRBSetRebalanceDetails.callAsync( + subjectTradingPool + ); + } - const auctionPriceParams = await rebalancingSetTokenV3.getAuctionPriceParameters.callAsync(); - const startingCurrentSets = await rebalancingSetTokenV3.startingCurrentSetAmount.callAsync(); - const biddingParams = await rebalancingSetTokenV3.getBiddingParameters.callAsync(); + it('fetches the correct poolInfo data', async () => { + const [ poolInfo, , ] = await subjectSocialTrading(); + + expect(poolInfo.trader).to.equal(trader); + expect(poolInfo.allocator).to.equal(allocator); + expect(poolInfo.currentAllocation).to.be.bignumber.equal(newAllocation); + expect(poolInfo.newEntryFee).to.be.bignumber.equal(ZERO); + expect(poolInfo.feeUpdateTimestamp).to.be.bignumber.equal(ZERO); + }); + + it('fetches the correct TradingPool data', async () => { + const [ , rbSetData, ] = await subjectSocialTrading(); + + const auctionPriceParams = await rebalancingSetToken.getAuctionPriceParameters.callAsync(); + const startingCurrentSets = await rebalancingSetToken.startingCurrentSetAmount.callAsync(); + const biddingParams = await rebalancingSetToken.getBiddingParameters.callAsync(); expect(rbSetData.rebalanceStartTime).to.be.bignumber.equal(auctionPriceParams[0]); expect(rbSetData.timeToPivot).to.be.bignumber.equal(auctionPriceParams[1]); @@ -651,11 +1013,11 @@ contract('ProtocolViewer', accounts => { expect(rbSetData.minimumBid).to.be.bignumber.equal(biddingParams[0]); expect(rbSetData.rebalanceState).to.be.bignumber.equal(new BigNumber(2)); expect(rbSetData.nextSet).to.equal(nextSet.address); - expect(rbSetData.liquidator).to.equal(twapLiquidator.address); + expect(rbSetData.liquidator).to.equal(liquidator.address); }); it('fetches the correct CollateralSet data', async () => { - const [ , collateralSetData ] = await subject(); + const [ , , collateralSetData ] = await subjectSocialTrading(); expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(set2Components)); expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(set2Units)); @@ -663,985 +1025,733 @@ contract('ProtocolViewer', accounts => { expect(collateralSetData.name).to.equal('Set Token'); expect(collateralSetData.symbol).to.equal('SET'); }); - }); - describe('Trading Pool Tests', async () => { - let newAllocation: BigNumber; + it('fetches the correct RebalancingSetTokenV2 data', async () => { + const [ rbSetData, ] = await subjectRBSet(); - describe('#fetchNewTradingPoolDetails', async () => { - let subjectTradingPool: Address; + const auctionPriceParams = await rebalancingSetToken.getAuctionPriceParameters.callAsync(); + const startingCurrentSets = await rebalancingSetToken.startingCurrentSetAmount.callAsync(); + const biddingParams = await rebalancingSetToken.getBiddingParameters.callAsync(); - let newFee: BigNumber; - let feeUpdateTimestamp: BigNumber; - beforeEach(async () => { - newFee = ether(.04); - await setManager.updateFee.sendTransactionAsync( - rebalancingSetToken.address, - newFee - ); + expect(rbSetData.rebalanceStartTime).to.be.bignumber.equal(auctionPriceParams[0]); + expect(rbSetData.timeToPivot).to.be.bignumber.equal(auctionPriceParams[1]); + expect(rbSetData.startPrice).to.be.bignumber.equal(auctionPriceParams[2]); + expect(rbSetData.endPrice).to.be.bignumber.equal(auctionPriceParams[3]); + expect(rbSetData.startingCurrentSets).to.be.bignumber.equal(startingCurrentSets); + expect(rbSetData.remainingCurrentSets).to.be.bignumber.equal(biddingParams[1]); + expect(rbSetData.minimumBid).to.be.bignumber.equal(biddingParams[0]); + expect(rbSetData.rebalanceState).to.be.bignumber.equal(new BigNumber(2)); + expect(rbSetData.nextSet).to.equal(nextSet.address); + expect(rbSetData.liquidator).to.equal(liquidator.address); + }); - const { timestamp } = await web3.eth.getBlock('latest'); - feeUpdateTimestamp = new BigNumber(timestamp); + it('fetches the correct CollateralSet data', async () => { + const [ , collateralSetData ] = await subjectRBSet(); - subjectTradingPool = rebalancingSetToken.address; - }); + expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(set2Components)); + expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(set2Units)); + expect(collateralSetData.naturalUnit).to.be.bignumber.equal(set2NaturalUnit); + expect(collateralSetData.name).to.equal('Set Token'); + expect(collateralSetData.symbol).to.equal('SET'); + }); + }); - async function subject(): Promise { - return protocolViewer.fetchNewTradingPoolDetails.callAsync( - subjectTradingPool - ); - } + describe('#fetchTradingPoolTWAPRebalanceDetails and fetchRBSetTWAPRebalanceDetails', async () => { + let subjectTradingPool: Address; - it('fetches the correct poolInfo data', async () => { - const [ poolInfo, , ] = await subject(); + beforeEach(async () => { + const currentAllocation = ether(.6); - expect(poolInfo.trader).to.equal(trader); - expect(poolInfo.allocator).to.equal(allocator); - expect(poolInfo.currentAllocation).to.be.bignumber.equal(currentAllocation); - expect(poolInfo.newEntryFee).to.be.bignumber.equal(newFee); - expect(poolInfo.feeUpdateTimestamp).to.be.bignumber.equal(feeUpdateTimestamp); - }); + await rebalancingSetTokenV3.setManager.sendTransactionAsync(setManager.address); - it('fetches the correct RebalancingSetTokenV2/TradingPool data', async () => { - const [ , rbSetData, ] = await subject(); - - expect(rbSetData.manager).to.equal(setManager.address); - expect(rbSetData.feeRecipient).to.equal(feeRecipient); - expect(rbSetData.currentSet).to.equal(currentSetToken.address); - expect(rbSetData.name).to.equal('Rebalancing Set Token'); - expect(rbSetData.symbol).to.equal('RBSET'); - expect(rbSetData.unitShares).to.be.bignumber.equal(DEFAULT_UNIT_SHARES); - expect(rbSetData.naturalUnit).to.be.bignumber.equal(DEFAULT_REBALANCING_NATURAL_UNIT); - expect(rbSetData.rebalanceInterval).to.be.bignumber.equal(ONE_DAY_IN_SECONDS); - expect(rbSetData.entryFee).to.be.bignumber.equal(entryFee); - expect(rbSetData.rebalanceFee).to.be.bignumber.equal(rebalanceFee); - expect(rbSetData.lastRebalanceTimestamp).to.be.bignumber.equal(lastRebalanceTimestamp); - expect(rbSetData.rebalanceState).to.be.bignumber.equal(ZERO); - }); + await setManager.updateRecord.sendTransactionAsync( + rebalancingSetTokenV3.address, + trader, + allocator, + currentAllocation + ); - it('fetches the correct CollateralSet data', async () => { - const [ , , collateralSetData ] = await subject(); + // Issue currentSetToken + await coreMock.issue.sendTransactionAsync( + currentSetToken.address, + ether(8), + {from: deployerAccount} + ); + await erc20Helper.approveTransfersAsync([currentSetToken], transferProxy.address); - expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(set1Components)); - expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(set1Units)); - expect(collateralSetData.naturalUnit).to.be.bignumber.equal(set1NaturalUnit); - expect(collateralSetData.name).to.equal('Set Token'); - expect(collateralSetData.symbol).to.equal('SET'); - }); - }); + // Use issued currentSetToken to issue rebalancingSetTokenV3 + const rebalancingSetQuantityToIssue = ether(7); + await coreMock.issue.sendTransactionAsync(rebalancingSetTokenV3.address, rebalancingSetQuantityToIssue); - describe('#fetchNewTradingPoolV2Details', async () => { - let subjectTradingPool: Address; + await blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS); - let ethOracleWhiteList: OracleWhiteListContract; - let usdOracleWhiteList: OracleWhiteListContract; + const liquidatorData = liquidatorHelper.generateTWAPLiquidatorCalldata( + ether(10 ** 6), + ONE_DAY_IN_SECONDS, + ); + nextSet = set2; + newAllocation = ether(.4); + await setManager.rebalance.sendTransactionAsync( + rebalancingSetTokenV3.address, + nextSet.address, + newAllocation, + liquidatorData + ); - let wrappedETH: StandardTokenMockContract; - let wrappedBTC: StandardTokenMockContract; - let usdc: StandardTokenMockContract; - let dai: StandardTokenMockContract; + subjectTradingPool = rebalancingSetTokenV3.address; + }); - let collateralSet: SetTokenContract; - let collateralSetComponents: Address[]; - let collateralSetUnits: BigNumber[]; - let collateralSetNaturalUnit: BigNumber; + async function subjectSocialTrading(): Promise { + return protocolViewer.fetchTradingPoolTWAPRebalanceDetails.callAsync( + subjectTradingPool + ); + } - let firstSetUnits: BigNumber; - let currentAllocation: BigNumber; + async function subjectRBSet(): Promise { + return protocolViewer.fetchRBSetTWAPRebalanceDetails.callAsync( + subjectTradingPool + ); + } - let usdWrappedETHOracle: UpdatableOracleMockContract; - let usdWrappedBTCOracle: UpdatableOracleMockContract; - let usdUSDCOracle: UpdatableOracleMockContract; - let usdDaiOracle: UpdatableOracleMockContract; + it('fetches the correct poolInfo data', async () => { + const [ poolInfo, , ] = await subjectSocialTrading(); - let ethWrappedETHOracle: UpdatableOracleMockContract; - let ethWrappedBTCOracle: UpdatableOracleMockContract; - let ethUSDCOracle: UpdatableOracleMockContract; - let ethDaiOracle: UpdatableOracleMockContract; + expect(poolInfo.trader).to.equal(trader); + expect(poolInfo.allocator).to.equal(allocator); + expect(poolInfo.currentAllocation).to.be.bignumber.equal(newAllocation); + expect(poolInfo.newEntryFee).to.be.bignumber.equal(ZERO); + expect(poolInfo.feeUpdateTimestamp).to.be.bignumber.equal(ZERO); + }); - let ethPerformanceFeeCalculator: PerformanceFeeCalculatorContract; + it('fetches the correct TradingPool data', async () => { + const [ , rbSetData, ] = await subjectSocialTrading(); - beforeEach(async () => { - wrappedETH = await erc20Helper.deployTokenAsync(deployerAccount, 18); - wrappedBTC = await erc20Helper.deployTokenAsync(deployerAccount, 8); - usdc = await erc20Helper.deployTokenAsync(deployerAccount, 6); - dai = await erc20Helper.deployTokenAsync(deployerAccount, 18); - - let wrappedETHPrice: BigNumber; - let wrappedBTCPrice: BigNumber; - let usdcPrice: BigNumber; - let daiPrice: BigNumber; - - wrappedETHPrice = ether(128); - wrappedBTCPrice = ether(7500); - usdcPrice = ether(1); - daiPrice = ether(1); - - usdWrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedETHPrice); - usdWrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedBTCPrice); - usdUSDCOracle = await oracleHelper.deployUpdatableOracleMockAsync(usdcPrice); - usdDaiOracle = await oracleHelper.deployUpdatableOracleMockAsync(daiPrice); - - usdOracleWhiteList = await coreHelper.deployOracleWhiteListAsync( - [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], - [usdWrappedETHOracle.address, usdWrappedBTCOracle.address, usdUSDCOracle.address, usdDaiOracle.address], - ); + const auctionPriceParams = await rebalancingSetTokenV3.getAuctionPriceParameters.callAsync(); + const startingCurrentSets = await rebalancingSetTokenV3.startingCurrentSetAmount.callAsync(); + const biddingParams = await rebalancingSetTokenV3.getBiddingParameters.callAsync(); - ethWrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync( - wrappedETHPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) - ); - ethWrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync( - wrappedBTCPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) - ); - ethUSDCOracle = await oracleHelper.deployUpdatableOracleMockAsync( - usdcPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) - ); - ethDaiOracle = await oracleHelper.deployUpdatableOracleMockAsync( - daiPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) - ); + expect(rbSetData.rebalanceStartTime).to.be.bignumber.equal(auctionPriceParams[0]); + expect(rbSetData.timeToPivot).to.be.bignumber.equal(auctionPriceParams[1]); + expect(rbSetData.startPrice).to.be.bignumber.equal(auctionPriceParams[2]); + expect(rbSetData.endPrice).to.be.bignumber.equal(auctionPriceParams[3]); + expect(rbSetData.startingCurrentSets).to.be.bignumber.equal(startingCurrentSets); + expect(rbSetData.remainingCurrentSets).to.be.bignumber.equal(biddingParams[1]); + expect(rbSetData.minimumBid).to.be.bignumber.equal(biddingParams[0]); + expect(rbSetData.rebalanceState).to.be.bignumber.equal(new BigNumber(2)); + expect(rbSetData.nextSet).to.equal(nextSet.address); + expect(rbSetData.liquidator).to.equal(twapLiquidator.address); + }); - ethOracleWhiteList = await coreHelper.deployOracleWhiteListAsync( - [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], - [ethWrappedETHOracle.address, ethWrappedBTCOracle.address, ethUSDCOracle.address, ethDaiOracle.address], - ); + it('fetches the correct CollateralSet data', async () => { + const [ , , collateralSetData ] = await subjectSocialTrading(); - const maxProfitFeePercentage = ether(.5); - const maxStreamingFeePercentage = ether(.1); - ethPerformanceFeeCalculator = await feeCalculatorHelper.deployPerformanceFeeCalculatorAsync( - coreMock.address, - ethOracleWhiteList.address, - maxProfitFeePercentage, - maxStreamingFeePercentage - ); - await coreHelper.addAddressToWhiteList(ethPerformanceFeeCalculator.address, feeCalculatorWhitelist); - - collateralSetComponents = [wrappedETH.address, wrappedBTC.address]; - collateralSetUnits = [wrappedBTCPrice.div(wrappedETHPrice).mul(10 ** 12), new BigNumber(100)]; - collateralSetNaturalUnit = new BigNumber(10 ** 12); - collateralSet = await coreHelper.createSetTokenAsync( - coreMock, - factory.address, - collateralSetComponents, - collateralSetUnits, - collateralSetNaturalUnit, - ); + expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(set2Components)); + expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(set2Units)); + expect(collateralSetData.naturalUnit).to.be.bignumber.equal(set2NaturalUnit); + expect(collateralSetData.name).to.equal('Set Token'); + expect(collateralSetData.symbol).to.equal('SET'); + }); + + it('fetches the correct RebalancingSetToken data', async () => { + const [ rbSetData, ] = await subjectRBSet(); + + const auctionPriceParams = await rebalancingSetTokenV3.getAuctionPriceParameters.callAsync(); + const startingCurrentSets = await rebalancingSetTokenV3.startingCurrentSetAmount.callAsync(); + const biddingParams = await rebalancingSetTokenV3.getBiddingParameters.callAsync(); - setManager = await viewerHelper.deploySocialTradingManagerMockAsync(); + expect(rbSetData.rebalanceStartTime).to.be.bignumber.equal(auctionPriceParams[0]); + expect(rbSetData.timeToPivot).to.be.bignumber.equal(auctionPriceParams[1]); + expect(rbSetData.startPrice).to.be.bignumber.equal(auctionPriceParams[2]); + expect(rbSetData.endPrice).to.be.bignumber.equal(auctionPriceParams[3]); + expect(rbSetData.startingCurrentSets).to.be.bignumber.equal(startingCurrentSets); + expect(rbSetData.remainingCurrentSets).to.be.bignumber.equal(biddingParams[1]); + expect(rbSetData.minimumBid).to.be.bignumber.equal(biddingParams[0]); + expect(rbSetData.rebalanceState).to.be.bignumber.equal(new BigNumber(2)); + expect(rbSetData.nextSet).to.equal(nextSet.address); + expect(rbSetData.liquidator).to.equal(twapLiquidator.address); + }); - const failPeriod = ONE_DAY_IN_SECONDS; - const { timestamp } = await web3.eth.getBlock('latest'); - lastRebalanceTimestamp = new BigNumber(timestamp); + it('fetches the correct CollateralSet data', async () => { + const [ , collateralSetData ] = await subjectRBSet(); - const calculatorData = feeCalculatorHelper.generatePerformanceFeeCallDataBuffer( - ONE_DAY_IN_SECONDS.mul(30), - ONE_YEAR_IN_SECONDS, - ether(.2), - ether(.02) - ); + expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(set2Components)); + expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(set2Units)); + expect(collateralSetData.naturalUnit).to.be.bignumber.equal(set2NaturalUnit); + expect(collateralSetData.name).to.equal('Set Token'); + expect(collateralSetData.symbol).to.equal('SET'); + }); + }); - const firstNaturalUnit = DEFAULT_REBALANCING_NATURAL_UNIT; - const firstSetValue = await valuationHelper.calculateSetTokenValueAsync(collateralSet, usdOracleWhiteList); - firstSetUnits = new BigNumber(100).mul(firstNaturalUnit).mul(10 ** 18).div(firstSetValue).round(0, 3); - const firstSetCallData = rebalancingSetV3Helper.generateRebalancingSetTokenV3CallData( - setManager.address, - liquidator.address, - feeRecipient, - ethPerformanceFeeCalculator.address, - ONE_DAY_IN_SECONDS, - failPeriod, - lastRebalanceTimestamp, - ZERO, - calculatorData - ); + describe('Trading Pool V1 Batch Fetches', async () => { + let rebalancingSetToken2: RebalancingSetTokenV2Contract; + let rebalanceFee2: BigNumber; + let entryFee2: BigNumber; - rebalancingSetToken = await rebalancingSetV3Helper.createRebalancingTokenV3Async( - coreMock, - rebalancingFactoryV3.address, - [collateralSet.address], - [firstSetUnits], - firstNaturalUnit, - firstSetCallData - ); + beforeEach(async () => { + const failPeriod = ONE_DAY_IN_SECONDS; + const { timestamp } = await web3.eth.getBlock('latest'); + const lastRebalanceTimestamp = timestamp; + + entryFee2 = ether(.03); + rebalanceFee2 = ether(.003); + rebalancingSetToken2 = await rebalancingHelper.createDefaultRebalancingSetTokenV2Async( + coreMock, + rebalancingFactoryV2.address, + setManager.address, + liquidator.address, + feeRecipient, + fixedFeeCalculator.address, + set1.address, + failPeriod, + new BigNumber(lastRebalanceTimestamp), + entryFee2, + rebalanceFee2 + ); + }); - currentAllocation = ether(.6); - await setManager.updateRecord.sendTransactionAsync( - rebalancingSetToken.address, - trader, - allocator, - currentAllocation - ); + describe('#batchFetchTradingPoolEntryFees', async () => { + let subjectTradingPools: Address[]; - subjectTradingPool = rebalancingSetToken.address; + beforeEach(async () => { + subjectTradingPools = [rebalancingSetToken.address, rebalancingSetToken2.address]; }); async function subject(): Promise { - return protocolViewer.fetchNewTradingPoolV2Details.callAsync( - subjectTradingPool + return protocolViewer.batchFetchTradingPoolEntryFees.callAsync( + subjectTradingPools ); } - it('fetches the correct RebalancingSetTokenV3/TradingPool data', async () => { - const [ , tradingPoolInfo, , , ] = await subject(); - - expect(tradingPoolInfo.manager).to.equal(setManager.address); - expect(tradingPoolInfo.feeRecipient).to.equal(feeRecipient); - expect(tradingPoolInfo.currentSet).to.equal(collateralSet.address); - expect(tradingPoolInfo.name).to.equal('Rebalancing Set Token'); - expect(tradingPoolInfo.symbol).to.equal('RBSET'); - expect(tradingPoolInfo.unitShares).to.be.bignumber.equal(firstSetUnits); - expect(tradingPoolInfo.naturalUnit).to.be.bignumber.equal(DEFAULT_REBALANCING_NATURAL_UNIT); - expect(tradingPoolInfo.rebalanceInterval).to.be.bignumber.equal(ONE_DAY_IN_SECONDS); - expect(tradingPoolInfo.entryFee).to.be.bignumber.equal(ZERO); - expect(tradingPoolInfo.lastRebalanceTimestamp).to.be.bignumber.equal(lastRebalanceTimestamp); - expect(tradingPoolInfo.rebalanceState).to.be.bignumber.equal(ZERO); - }); + it('fetches the correct entryFee array', async () => { + const actualEntryFeeArray = await subject(); - it('fetches the correct poolInfo data', async () => { - const [ poolInfo, , , , ] = await subject(); + const expectedEntryFeeArray = [entryFee, entryFee2]; - expect(poolInfo.trader).to.equal(trader); - expect(poolInfo.allocator).to.equal(allocator); - expect(poolInfo.currentAllocation).to.be.bignumber.equal(currentAllocation); - expect(poolInfo.newEntryFee).to.be.bignumber.equal(ZERO); - expect(poolInfo.feeUpdateTimestamp).to.be.bignumber.equal(ZERO); - }); - - - it('fetches the correct RebalancingSetTokenV3/Performance Fee data', async () => { - const [ , , performanceFeeState, , ] = await subject(); - const [ - profitFeePeriod, - highWatermarkResetPeriod, - profitFeePercentage, - streamingFeePercentage, - highWatermark, - lastProfitFeeTimestamp, - lastStreamingFeeTimestamp, - ] = performanceFeeState; - - const expectedFeeStates: any = - await ethPerformanceFeeCalculator.feeState.callAsync(rebalancingSetToken.address); - - expect(profitFeePeriod).to.equal(expectedFeeStates.profitFeePeriod); - expect(highWatermarkResetPeriod).to.equal(expectedFeeStates.highWatermarkResetPeriod); - expect(profitFeePercentage).to.equal(expectedFeeStates.profitFeePercentage); - expect(streamingFeePercentage).to.equal(expectedFeeStates.streamingFeePercentage); - expect(highWatermark).to.equal(expectedFeeStates.highWatermark); - expect(lastProfitFeeTimestamp).to.equal(expectedFeeStates.lastProfitFeeTimestamp); - expect(lastStreamingFeeTimestamp).to.equal(expectedFeeStates.lastStreamingFeeTimestamp); - }); - - it('fetches the correct CollateralSet data', async () => { - const [ , , , collateralSetData, ] = await subject(); - - expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(collateralSetComponents)); - expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(collateralSetUnits)); - expect(collateralSetData.naturalUnit).to.be.bignumber.equal(collateralSetNaturalUnit); - expect(collateralSetData.name).to.equal('Set Token'); - expect(collateralSetData.symbol).to.equal('SET'); - }); - - it('fetches the correct PerformanceFeeCalculator address', async () => { - const [ , , , , performanceFeeCalculatorAddress ] = await subject(); - - expect(performanceFeeCalculatorAddress).to.equal(ethPerformanceFeeCalculator.address); + expect(JSON.stringify(actualEntryFeeArray)).to.equal(JSON.stringify(expectedEntryFeeArray)); }); }); - describe('#fetchTradingPoolRebalanceDetails', async () => { - let subjectTradingPool: Address; + describe('#batchFetchTradingPoolRebalanceFees', async () => { + let subjectTradingPools: Address[]; beforeEach(async () => { - // Issue currentSetToken - await coreMock.issue.sendTransactionAsync( - currentSetToken.address, - ether(8), - {from: deployerAccount} - ); - await erc20Helper.approveTransfersAsync([currentSetToken], transferProxy.address); - - // Use issued currentSetToken to issue rebalancingSetToken - const rebalancingSetQuantityToIssue = ether(7); - await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, rebalancingSetQuantityToIssue); - - await blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS); - - - const liquidatorData = '0x'; - nextSet = set2; - newAllocation = ether(.4); - await setManager.rebalance.sendTransactionAsync( - rebalancingSetToken.address, - nextSet.address, - newAllocation, - liquidatorData - ); - - subjectTradingPool = rebalancingSetToken.address; + subjectTradingPools = [rebalancingSetToken.address, rebalancingSetToken2.address]; }); async function subject(): Promise { - return protocolViewer.fetchTradingPoolRebalanceDetails.callAsync( - subjectTradingPool + return protocolViewer.batchFetchTradingPoolRebalanceFees.callAsync( + subjectTradingPools ); } - it('fetches the correct poolInfo data', async () => { - const [ poolInfo, , ] = await subject(); - - expect(poolInfo.trader).to.equal(trader); - expect(poolInfo.allocator).to.equal(allocator); - expect(poolInfo.currentAllocation).to.be.bignumber.equal(newAllocation); - expect(poolInfo.newEntryFee).to.be.bignumber.equal(ZERO); - expect(poolInfo.feeUpdateTimestamp).to.be.bignumber.equal(ZERO); - }); - - it('fetches the correct RebalancingSetTokenV2/TradingPool data', async () => { - const [ , rbSetData, ] = await subject(); - - const auctionPriceParams = await rebalancingSetToken.getAuctionPriceParameters.callAsync(); - const startingCurrentSets = await rebalancingSetToken.startingCurrentSetAmount.callAsync(); - const biddingParams = await rebalancingSetToken.getBiddingParameters.callAsync(); - - expect(rbSetData.rebalanceStartTime).to.be.bignumber.equal(auctionPriceParams[0]); - expect(rbSetData.timeToPivot).to.be.bignumber.equal(auctionPriceParams[1]); - expect(rbSetData.startPrice).to.be.bignumber.equal(auctionPriceParams[2]); - expect(rbSetData.endPrice).to.be.bignumber.equal(auctionPriceParams[3]); - expect(rbSetData.startingCurrentSets).to.be.bignumber.equal(startingCurrentSets); - expect(rbSetData.remainingCurrentSets).to.be.bignumber.equal(biddingParams[1]); - expect(rbSetData.minimumBid).to.be.bignumber.equal(biddingParams[0]); - expect(rbSetData.rebalanceState).to.be.bignumber.equal(new BigNumber(2)); - expect(rbSetData.nextSet).to.equal(nextSet.address); - expect(rbSetData.liquidator).to.equal(liquidator.address); - }); + it('fetches the correct rebalanceFee array', async () => { + const actualEntryRebalanceArray = await subject(); - it('fetches the correct CollateralSet data', async () => { - const [ , , collateralSetData ] = await subject(); + const expectedEntryRebalanceArray = [rebalanceFee, rebalanceFee2]; - expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(set2Components)); - expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(set2Units)); - expect(collateralSetData.naturalUnit).to.be.bignumber.equal(set2NaturalUnit); - expect(collateralSetData.name).to.equal('Set Token'); - expect(collateralSetData.symbol).to.equal('SET'); + expect(JSON.stringify(actualEntryRebalanceArray)).to.equal(JSON.stringify(expectedEntryRebalanceArray)); }); }); - describe('#fetchTradingPoolTWAPRebalanceDetails', async () => { - let subjectTradingPool: Address; + describe('#batchFetchTradingPoolOperator', async () => { + let subjectTradingPools: Address[]; beforeEach(async () => { - const currentAllocation = ether(.6); - - await rebalancingSetTokenV3.setManager.sendTransactionAsync(setManager.address); - await setManager.updateRecord.sendTransactionAsync( - rebalancingSetTokenV3.address, - trader, + rebalancingSetToken.address, + trader, // Set to first trader allocator, - currentAllocation + ether(.3) ); - // Issue currentSetToken - await coreMock.issue.sendTransactionAsync( - currentSetToken.address, - ether(8), - {from: deployerAccount} - ); - await erc20Helper.approveTransfersAsync([currentSetToken], transferProxy.address); - - // Use issued currentSetToken to issue rebalancingSetTokenV3 - const rebalancingSetQuantityToIssue = ether(7); - await coreMock.issue.sendTransactionAsync(rebalancingSetTokenV3.address, rebalancingSetQuantityToIssue); - - await blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS); - - const liquidatorData = liquidatorHelper.generateTWAPLiquidatorCalldata( - ether(10 ** 6), - ONE_DAY_IN_SECONDS, - ); - nextSet = set2; - newAllocation = ether(.4); - await setManager.rebalance.sendTransactionAsync( - rebalancingSetTokenV3.address, - nextSet.address, - newAllocation, - liquidatorData + await setManager.updateRecord.sendTransactionAsync( + rebalancingSetToken2.address, + trader2, // Set to second trader + allocator, + ether(.6) ); - subjectTradingPool = rebalancingSetTokenV3.address; + subjectTradingPools = [rebalancingSetToken.address, rebalancingSetToken2.address]; }); async function subject(): Promise { - return protocolViewer.fetchTradingPoolTWAPRebalanceDetails.callAsync( - subjectTradingPool + return protocolViewer.batchFetchTradingPoolOperator.callAsync( + subjectTradingPools ); } - it('fetches the correct poolInfo data', async () => { - const [ poolInfo, , ] = await subject(); + it('fetches the correct operators array', async () => { + const actualOperatorsArray = await subject(); - expect(poolInfo.trader).to.equal(trader); - expect(poolInfo.allocator).to.equal(allocator); - expect(poolInfo.currentAllocation).to.be.bignumber.equal(newAllocation); - expect(poolInfo.newEntryFee).to.be.bignumber.equal(ZERO); - expect(poolInfo.feeUpdateTimestamp).to.be.bignumber.equal(ZERO); - }); + const expectedOperatorsArray = [trader, trader2]; - it('fetches the correct RebalancingSetTokenV2/TradingPool data', async () => { - const [ , rbSetData, ] = await subject(); - - const auctionPriceParams = await rebalancingSetTokenV3.getAuctionPriceParameters.callAsync(); - const startingCurrentSets = await rebalancingSetTokenV3.startingCurrentSetAmount.callAsync(); - const biddingParams = await rebalancingSetTokenV3.getBiddingParameters.callAsync(); - - expect(rbSetData.rebalanceStartTime).to.be.bignumber.equal(auctionPriceParams[0]); - expect(rbSetData.timeToPivot).to.be.bignumber.equal(auctionPriceParams[1]); - expect(rbSetData.startPrice).to.be.bignumber.equal(auctionPriceParams[2]); - expect(rbSetData.endPrice).to.be.bignumber.equal(auctionPriceParams[3]); - expect(rbSetData.startingCurrentSets).to.be.bignumber.equal(startingCurrentSets); - expect(rbSetData.remainingCurrentSets).to.be.bignumber.equal(biddingParams[1]); - expect(rbSetData.minimumBid).to.be.bignumber.equal(biddingParams[0]); - expect(rbSetData.rebalanceState).to.be.bignumber.equal(new BigNumber(2)); - expect(rbSetData.nextSet).to.equal(nextSet.address); - expect(rbSetData.liquidator).to.equal(twapLiquidator.address); - }); - - it('fetches the correct CollateralSet data', async () => { - const [ , , collateralSetData ] = await subject(); - - expect(JSON.stringify(collateralSetData.components)).to.equal(JSON.stringify(set2Components)); - expect(JSON.stringify(collateralSetData.units)).to.equal(JSON.stringify(set2Units)); - expect(collateralSetData.naturalUnit).to.be.bignumber.equal(set2NaturalUnit); - expect(collateralSetData.name).to.equal('Set Token'); - expect(collateralSetData.symbol).to.equal('SET'); + expect(JSON.stringify(actualOperatorsArray)).to.equal(JSON.stringify(expectedOperatorsArray)); }); }); + }); - describe('Trading Pool V1 Batch Fetches', async () => { - let rebalancingSetToken2: RebalancingSetTokenV2Contract; - let rebalanceFee2: BigNumber; - let entryFee2: BigNumber; - - beforeEach(async () => { - const failPeriod = ONE_DAY_IN_SECONDS; - const { timestamp } = await web3.eth.getBlock('latest'); - const lastRebalanceTimestamp = timestamp; - - entryFee2 = ether(.03); - rebalanceFee2 = ether(.003); - rebalancingSetToken2 = await rebalancingHelper.createDefaultRebalancingSetTokenV2Async( - coreMock, - rebalancingFactoryV2.address, - setManager.address, - liquidator.address, - feeRecipient, - fixedFeeCalculator.address, - set1.address, - failPeriod, - new BigNumber(lastRebalanceTimestamp), - entryFee2, - rebalanceFee2 - ); - }); - - describe('#batchFetchTradingPoolEntryFees', async () => { - let subjectTradingPools: Address[]; + describe('#batchFetchTradingPoolAccumulation', async () => { + let subjectTradingPools: Address[]; - beforeEach(async () => { - subjectTradingPools = [rebalancingSetToken.address, rebalancingSetToken2.address]; - }); + let ethOracleWhiteList: OracleWhiteListContract; + let usdOracleWhiteList: OracleWhiteListContract; - async function subject(): Promise { - return protocolViewer.batchFetchTradingPoolEntryFees.callAsync( - subjectTradingPools - ); - } + let wrappedETH: StandardTokenMockContract; + let wrappedBTC: StandardTokenMockContract; + let usdc: StandardTokenMockContract; + let dai: StandardTokenMockContract; - it('fetches the correct entryFee array', async () => { - const actualEntryFeeArray = await subject(); + let collateralSet: SetTokenContract; - const expectedEntryFeeArray = [entryFee, entryFee2]; + let usdWrappedETHOracle: UpdatableOracleMockContract; + let usdWrappedBTCOracle: UpdatableOracleMockContract; + let usdUSDCOracle: UpdatableOracleMockContract; + let usdDaiOracle: UpdatableOracleMockContract; - expect(JSON.stringify(actualEntryFeeArray)).to.equal(JSON.stringify(expectedEntryFeeArray)); - }); - }); + let ethWrappedETHOracle: UpdatableOracleMockContract; + let ethWrappedBTCOracle: UpdatableOracleMockContract; + let ethUSDCOracle: UpdatableOracleMockContract; + let ethDaiOracle: UpdatableOracleMockContract; - describe('#batchFetchTradingPoolRebalanceFees', async () => { - let subjectTradingPools: Address[]; + let ethPerformanceFeeCalculator: PerformanceFeeCalculatorContract; + let usdPerformanceFeeCalculator: PerformanceFeeCalculatorContract; - beforeEach(async () => { - subjectTradingPools = [rebalancingSetToken.address, rebalancingSetToken2.address]; - }); + let rebalancingSetToken2: RebalancingSetTokenV3Contract; - async function subject(): Promise { - return protocolViewer.batchFetchTradingPoolRebalanceFees.callAsync( - subjectTradingPools - ); - } + let subjectIncreaseChainTime: BigNumber; - it('fetches the correct rebalanceFee array', async () => { - const actualEntryRebalanceArray = await subject(); + beforeEach(async () => { + wrappedETH = await erc20Helper.deployTokenAsync(deployerAccount, 18); + wrappedBTC = await erc20Helper.deployTokenAsync(deployerAccount, 8); + usdc = await erc20Helper.deployTokenAsync(deployerAccount, 6); + dai = await erc20Helper.deployTokenAsync(deployerAccount, 18); + + let wrappedETHPrice: BigNumber; + let wrappedBTCPrice: BigNumber; + let usdcPrice: BigNumber; + let daiPrice: BigNumber; + + wrappedETHPrice = ether(128); + wrappedBTCPrice = ether(7500); + usdcPrice = ether(1); + daiPrice = ether(1); + + usdWrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedETHPrice); + usdWrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedBTCPrice); + usdUSDCOracle = await oracleHelper.deployUpdatableOracleMockAsync(usdcPrice); + usdDaiOracle = await oracleHelper.deployUpdatableOracleMockAsync(daiPrice); + + usdOracleWhiteList = await coreHelper.deployOracleWhiteListAsync( + [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], + [usdWrappedETHOracle.address, usdWrappedBTCOracle.address, usdUSDCOracle.address, usdDaiOracle.address], + ); - const expectedEntryRebalanceArray = [rebalanceFee, rebalanceFee2]; + ethWrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync( + wrappedETHPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) + ); + ethWrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync( + wrappedBTCPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) + ); + ethUSDCOracle = await oracleHelper.deployUpdatableOracleMockAsync( + usdcPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) + ); + ethDaiOracle = await oracleHelper.deployUpdatableOracleMockAsync( + daiPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) + ); - expect(JSON.stringify(actualEntryRebalanceArray)).to.equal(JSON.stringify(expectedEntryRebalanceArray)); - }); - }); + ethOracleWhiteList = await coreHelper.deployOracleWhiteListAsync( + [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], + [ethWrappedETHOracle.address, ethWrappedBTCOracle.address, ethUSDCOracle.address, ethDaiOracle.address], + ); - describe('#batchFetchTradingPoolOperator', async () => { - let subjectTradingPools: Address[]; + const maxProfitFeePercentage = ether(.5); + const maxStreamingFeePercentage = ether(.1); + ethPerformanceFeeCalculator = await feeCalculatorHelper.deployPerformanceFeeCalculatorAsync( + coreMock.address, + ethOracleWhiteList.address, + maxProfitFeePercentage, + maxStreamingFeePercentage + ); + await coreHelper.addAddressToWhiteList(ethPerformanceFeeCalculator.address, feeCalculatorWhitelist); + + const collateralSetComponents = [wrappedETH.address, wrappedBTC.address]; + const collateralSetUnits = [wrappedBTCPrice.div(wrappedETHPrice).mul(10 ** 12), new BigNumber(100)]; + const collateralSetNaturalUnit = new BigNumber(10 ** 12); + collateralSet = await coreHelper.createSetTokenAsync( + coreMock, + factory.address, + collateralSetComponents, + collateralSetUnits, + collateralSetNaturalUnit, + ); - beforeEach(async () => { - await setManager.updateRecord.sendTransactionAsync( - rebalancingSetToken.address, - trader, // Set to first trader - allocator, - ether(.3) - ); + const failPeriod = ONE_DAY_IN_SECONDS; + const { timestamp } = await web3.eth.getBlock('latest'); + const lastRebalanceTimestamp = new BigNumber(timestamp); - await setManager.updateRecord.sendTransactionAsync( - rebalancingSetToken2.address, - trader2, // Set to second trader - allocator, - ether(.6) - ); + const calculatorData = feeCalculatorHelper.generatePerformanceFeeCallDataBuffer( + ONE_DAY_IN_SECONDS.mul(30), + ONE_YEAR_IN_SECONDS, + ether(.2), + ether(.02) + ); - subjectTradingPools = [rebalancingSetToken.address, rebalancingSetToken2.address]; - }); + const firstNaturalUnit = DEFAULT_REBALANCING_NATURAL_UNIT; + const firstSetValue = await valuationHelper.calculateSetTokenValueAsync(collateralSet, usdOracleWhiteList); + const firstSetUnits = new BigNumber(100).mul(firstNaturalUnit).mul(10 ** 18).div(firstSetValue).round(0, 3); + const firstSetCallData = rebalancingSetV3Helper.generateRebalancingSetTokenV3CallData( + deployerAccount, + liquidator.address, + feeRecipient, + ethPerformanceFeeCalculator.address, + ONE_DAY_IN_SECONDS, + failPeriod, + lastRebalanceTimestamp, + ZERO, + calculatorData + ); - async function subject(): Promise { - return protocolViewer.batchFetchTradingPoolOperator.callAsync( - subjectTradingPools - ); - } + rebalancingSetToken = await rebalancingSetV3Helper.createRebalancingTokenV3Async( + coreMock, + rebalancingFactoryV3.address, + [collateralSet.address], + [firstSetUnits], + firstNaturalUnit, + firstSetCallData + ); - it('fetches the correct operators array', async () => { - const actualOperatorsArray = await subject(); + usdPerformanceFeeCalculator = await feeCalculatorHelper.deployPerformanceFeeCalculatorAsync( + coreMock.address, + usdOracleWhiteList.address, + maxProfitFeePercentage, + maxStreamingFeePercentage + ); + await coreHelper.addAddressToWhiteList(usdPerformanceFeeCalculator.address, feeCalculatorWhitelist); + + const secondNaturalUnit = new BigNumber(10 ** 8); + const secondSetValue = await valuationHelper.calculateSetTokenValueAsync(collateralSet, usdOracleWhiteList); + const secondSetUnits = new BigNumber(100) + .mul(secondNaturalUnit) + .mul(10 ** 18) + .div(secondSetValue) + .round(0, 3); + const secondSetCallData = rebalancingSetV3Helper.generateRebalancingSetTokenV3CallData( + deployerAccount, + liquidator.address, + deployerAccount, + usdPerformanceFeeCalculator.address, + ONE_DAY_IN_SECONDS, + ONE_DAY_IN_SECONDS.mul(2), + ZERO, + ZERO, + calculatorData + ); - const expectedOperatorsArray = [trader, trader2]; + rebalancingSetToken2 = await rebalancingSetV3Helper.createRebalancingTokenV3Async( + coreMock, + rebalancingFactoryV3.address, + [collateralSet.address], + [secondSetUnits], + secondNaturalUnit, + secondSetCallData + ); - expect(JSON.stringify(actualOperatorsArray)).to.equal(JSON.stringify(expectedOperatorsArray)); - }); - }); + subjectTradingPools = [rebalancingSetToken.address, rebalancingSetToken2.address]; + subjectIncreaseChainTime = ONE_YEAR_IN_SECONDS; }); - describe('#batchFetchTradingPoolAccumulation', async () => { - let subjectTradingPools: Address[]; - - let ethOracleWhiteList: OracleWhiteListContract; - let usdOracleWhiteList: OracleWhiteListContract; - - let wrappedETH: StandardTokenMockContract; - let wrappedBTC: StandardTokenMockContract; - let usdc: StandardTokenMockContract; - let dai: StandardTokenMockContract; + async function subject(): Promise { + await blockchain.increaseTimeAsync(subjectIncreaseChainTime); + await blockchain.mineBlockAsync(); + return protocolViewer.batchFetchTradingPoolAccumulation.callAsync( + subjectTradingPools + ); + } - let collateralSet: SetTokenContract; + it('fetches the correct profit/streaming fee accumulation array', async () => { + const feeState1: any = await ethPerformanceFeeCalculator.feeState.callAsync(rebalancingSetToken.address); + const feeState2: any = await usdPerformanceFeeCalculator.feeState.callAsync(rebalancingSetToken2.address); - let usdWrappedETHOracle: UpdatableOracleMockContract; - let usdWrappedBTCOracle: UpdatableOracleMockContract; - let usdUSDCOracle: UpdatableOracleMockContract; - let usdDaiOracle: UpdatableOracleMockContract; + const [ + actualStreamingFeeArray, + actualProfitFeeArray, + ] = await subject(); - let ethWrappedETHOracle: UpdatableOracleMockContract; - let ethWrappedBTCOracle: UpdatableOracleMockContract; - let ethUSDCOracle: UpdatableOracleMockContract; - let ethDaiOracle: UpdatableOracleMockContract; + const lastBlock = await web3.eth.getBlock('latest'); - let ethPerformanceFeeCalculator: PerformanceFeeCalculatorContract; - let usdPerformanceFeeCalculator: PerformanceFeeCalculatorContract; + const rebalancingSetValue1 = await valuationHelper.calculateRebalancingSetTokenValueAsync( + rebalancingSetToken, + ethOracleWhiteList, + ); + const rebalancingSetValue2 = await valuationHelper.calculateRebalancingSetTokenValueAsync( + rebalancingSetToken2, + usdOracleWhiteList, + ); + const expectedStreamingFee1 = await feeCalculatorHelper.calculateAccruedStreamingFee( + feeState1.streamingFeePercentage, + new BigNumber(lastBlock.timestamp).sub(feeState1.lastStreamingFeeTimestamp) + ); + const expectedStreamingFee2 = await feeCalculatorHelper.calculateAccruedStreamingFee( + feeState2.streamingFeePercentage, + new BigNumber(lastBlock.timestamp).sub(feeState2.lastStreamingFeeTimestamp) + ); - let rebalancingSetToken2: RebalancingSetTokenV3Contract; + const expectedProfitFee1 = await feeCalculatorHelper.calculateAccruedProfitFeeAsync( + feeState1, + rebalancingSetValue1, + new BigNumber(lastBlock.timestamp) + ); + const expectedProfitFee2 = await feeCalculatorHelper.calculateAccruedProfitFeeAsync( + feeState2, + rebalancingSetValue2, + new BigNumber(lastBlock.timestamp) + ); - let subjectIncreaseChainTime: BigNumber; + const expectedStreamingFeeArray = [expectedStreamingFee1, expectedStreamingFee2]; + const expectedProfitFeeArray = [expectedProfitFee1, expectedProfitFee2]; - beforeEach(async () => { - wrappedETH = await erc20Helper.deployTokenAsync(deployerAccount, 18); - wrappedBTC = await erc20Helper.deployTokenAsync(deployerAccount, 8); - usdc = await erc20Helper.deployTokenAsync(deployerAccount, 6); - dai = await erc20Helper.deployTokenAsync(deployerAccount, 18); - - let wrappedETHPrice: BigNumber; - let wrappedBTCPrice: BigNumber; - let usdcPrice: BigNumber; - let daiPrice: BigNumber; - - wrappedETHPrice = ether(128); - wrappedBTCPrice = ether(7500); - usdcPrice = ether(1); - daiPrice = ether(1); - - usdWrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedETHPrice); - usdWrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedBTCPrice); - usdUSDCOracle = await oracleHelper.deployUpdatableOracleMockAsync(usdcPrice); - usdDaiOracle = await oracleHelper.deployUpdatableOracleMockAsync(daiPrice); - - usdOracleWhiteList = await coreHelper.deployOracleWhiteListAsync( - [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], - [usdWrappedETHOracle.address, usdWrappedBTCOracle.address, usdUSDCOracle.address, usdDaiOracle.address], - ); + expect(JSON.stringify(actualStreamingFeeArray)).to.equal(JSON.stringify(expectedStreamingFeeArray)); + expect(JSON.stringify(actualProfitFeeArray)).to.equal(JSON.stringify(expectedProfitFeeArray)); + }); + }); - ethWrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync( - wrappedETHPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) - ); - ethWrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync( - wrappedBTCPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) - ); - ethUSDCOracle = await oracleHelper.deployUpdatableOracleMockAsync( - usdcPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) - ); - ethDaiOracle = await oracleHelper.deployUpdatableOracleMockAsync( - daiPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) - ); + describe('#batchFetchTradingPoolFeeState', async () => { + let subjectTradingPools: Address[]; - ethOracleWhiteList = await coreHelper.deployOracleWhiteListAsync( - [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], - [ethWrappedETHOracle.address, ethWrappedBTCOracle.address, ethUSDCOracle.address, ethDaiOracle.address], - ); + let ethOracleWhiteList: OracleWhiteListContract; + let usdOracleWhiteList: OracleWhiteListContract; - const maxProfitFeePercentage = ether(.5); - const maxStreamingFeePercentage = ether(.1); - ethPerformanceFeeCalculator = await feeCalculatorHelper.deployPerformanceFeeCalculatorAsync( - coreMock.address, - ethOracleWhiteList.address, - maxProfitFeePercentage, - maxStreamingFeePercentage - ); - await coreHelper.addAddressToWhiteList(ethPerformanceFeeCalculator.address, feeCalculatorWhitelist); - - const collateralSetComponents = [wrappedETH.address, wrappedBTC.address]; - const collateralSetUnits = [wrappedBTCPrice.div(wrappedETHPrice).mul(10 ** 12), new BigNumber(100)]; - const collateralSetNaturalUnit = new BigNumber(10 ** 12); - collateralSet = await coreHelper.createSetTokenAsync( - coreMock, - factory.address, - collateralSetComponents, - collateralSetUnits, - collateralSetNaturalUnit, - ); + let wrappedETH: StandardTokenMockContract; + let wrappedBTC: StandardTokenMockContract; + let usdc: StandardTokenMockContract; + let dai: StandardTokenMockContract; - const failPeriod = ONE_DAY_IN_SECONDS; - const { timestamp } = await web3.eth.getBlock('latest'); - const lastRebalanceTimestamp = new BigNumber(timestamp); + let collateralSet: SetTokenContract; - const calculatorData = feeCalculatorHelper.generatePerformanceFeeCallDataBuffer( - ONE_DAY_IN_SECONDS.mul(30), - ONE_YEAR_IN_SECONDS, - ether(.2), - ether(.02) - ); + let usdWrappedETHOracle: UpdatableOracleMockContract; + let usdWrappedBTCOracle: UpdatableOracleMockContract; + let usdUSDCOracle: UpdatableOracleMockContract; + let usdDaiOracle: UpdatableOracleMockContract; - const firstNaturalUnit = DEFAULT_REBALANCING_NATURAL_UNIT; - const firstSetValue = await valuationHelper.calculateSetTokenValueAsync(collateralSet, usdOracleWhiteList); - const firstSetUnits = new BigNumber(100).mul(firstNaturalUnit).mul(10 ** 18).div(firstSetValue).round(0, 3); - const firstSetCallData = rebalancingSetV3Helper.generateRebalancingSetTokenV3CallData( - deployerAccount, - liquidator.address, - feeRecipient, - ethPerformanceFeeCalculator.address, - ONE_DAY_IN_SECONDS, - failPeriod, - lastRebalanceTimestamp, - ZERO, - calculatorData - ); + let ethWrappedETHOracle: UpdatableOracleMockContract; + let ethWrappedBTCOracle: UpdatableOracleMockContract; + let ethUSDCOracle: UpdatableOracleMockContract; + let ethDaiOracle: UpdatableOracleMockContract; - rebalancingSetToken = await rebalancingSetV3Helper.createRebalancingTokenV3Async( - coreMock, - rebalancingFactoryV3.address, - [collateralSet.address], - [firstSetUnits], - firstNaturalUnit, - firstSetCallData - ); + let ethPerformanceFeeCalculator: PerformanceFeeCalculatorContract; + let usdPerformanceFeeCalculator: PerformanceFeeCalculatorContract; - usdPerformanceFeeCalculator = await feeCalculatorHelper.deployPerformanceFeeCalculatorAsync( - coreMock.address, - usdOracleWhiteList.address, - maxProfitFeePercentage, - maxStreamingFeePercentage - ); - await coreHelper.addAddressToWhiteList(usdPerformanceFeeCalculator.address, feeCalculatorWhitelist); - - const secondNaturalUnit = new BigNumber(10 ** 8); - const secondSetValue = await valuationHelper.calculateSetTokenValueAsync(collateralSet, usdOracleWhiteList); - const secondSetUnits = new BigNumber(100) - .mul(secondNaturalUnit) - .mul(10 ** 18) - .div(secondSetValue) - .round(0, 3); - const secondSetCallData = rebalancingSetV3Helper.generateRebalancingSetTokenV3CallData( - deployerAccount, - liquidator.address, - deployerAccount, - usdPerformanceFeeCalculator.address, - ONE_DAY_IN_SECONDS, - ONE_DAY_IN_SECONDS.mul(2), - ZERO, - ZERO, - calculatorData - ); + let secondRebalancingSetToken: RebalancingSetTokenV3Contract; - rebalancingSetToken2 = await rebalancingSetV3Helper.createRebalancingTokenV3Async( - coreMock, - rebalancingFactoryV3.address, - [collateralSet.address], - [secondSetUnits], - secondNaturalUnit, - secondSetCallData - ); + beforeEach(async () => { + wrappedETH = await erc20Helper.deployTokenAsync(deployerAccount, 18); + wrappedBTC = await erc20Helper.deployTokenAsync(deployerAccount, 8); + usdc = await erc20Helper.deployTokenAsync(deployerAccount, 6); + dai = await erc20Helper.deployTokenAsync(deployerAccount, 18); + + let wrappedETHPrice: BigNumber; + let wrappedBTCPrice: BigNumber; + let usdcPrice: BigNumber; + let daiPrice: BigNumber; + + wrappedETHPrice = ether(128); + wrappedBTCPrice = ether(7500); + usdcPrice = ether(1); + daiPrice = ether(1); + + usdWrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedETHPrice); + usdWrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedBTCPrice); + usdUSDCOracle = await oracleHelper.deployUpdatableOracleMockAsync(usdcPrice); + usdDaiOracle = await oracleHelper.deployUpdatableOracleMockAsync(daiPrice); + + usdOracleWhiteList = await coreHelper.deployOracleWhiteListAsync( + [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], + [usdWrappedETHOracle.address, usdWrappedBTCOracle.address, usdUSDCOracle.address, usdDaiOracle.address], + ); - subjectTradingPools = [rebalancingSetToken.address, rebalancingSetToken2.address]; - subjectIncreaseChainTime = ONE_YEAR_IN_SECONDS; - }); + ethWrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync( + wrappedETHPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) + ); + ethWrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync( + wrappedBTCPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) + ); + ethUSDCOracle = await oracleHelper.deployUpdatableOracleMockAsync( + usdcPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) + ); + ethDaiOracle = await oracleHelper.deployUpdatableOracleMockAsync( + daiPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) + ); - async function subject(): Promise { - await blockchain.increaseTimeAsync(subjectIncreaseChainTime); - await blockchain.mineBlockAsync(); - return protocolViewer.batchFetchTradingPoolAccumulation.callAsync( - subjectTradingPools - ); - } + ethOracleWhiteList = await coreHelper.deployOracleWhiteListAsync( + [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], + [ethWrappedETHOracle.address, ethWrappedBTCOracle.address, ethUSDCOracle.address, ethDaiOracle.address], + ); - it('fetches the correct profit/streaming fee accumulation array', async () => { - const feeState1: any = await ethPerformanceFeeCalculator.feeState.callAsync(rebalancingSetToken.address); - const feeState2: any = await usdPerformanceFeeCalculator.feeState.callAsync(rebalancingSetToken2.address); + const maxProfitFeePercentage = ether(.5); + const maxStreamingFeePercentage = ether(.1); + ethPerformanceFeeCalculator = await feeCalculatorHelper.deployPerformanceFeeCalculatorAsync( + coreMock.address, + ethOracleWhiteList.address, + maxProfitFeePercentage, + maxStreamingFeePercentage + ); + await coreHelper.addAddressToWhiteList(ethPerformanceFeeCalculator.address, feeCalculatorWhitelist); + + const collateralSetComponents = [wrappedETH.address, wrappedBTC.address]; + const collateralSetUnits = [wrappedBTCPrice.div(wrappedETHPrice).mul(10 ** 12), new BigNumber(100)]; + const collateralSetNaturalUnit = new BigNumber(10 ** 12); + collateralSet = await coreHelper.createSetTokenAsync( + coreMock, + factory.address, + collateralSetComponents, + collateralSetUnits, + collateralSetNaturalUnit, + ); - const [ - actualStreamingFeeArray, - actualProfitFeeArray, - ] = await subject(); + const calculatorData = feeCalculatorHelper.generatePerformanceFeeCallDataBuffer( + ONE_DAY_IN_SECONDS.mul(30), + ONE_YEAR_IN_SECONDS, + ether(.2), + ether(.02) + ); - const lastBlock = await web3.eth.getBlock('latest'); + const firstNaturalUnit = new BigNumber(10 ** 8); + const firstSetValue = await valuationHelper.calculateSetTokenValueAsync(collateralSet, usdOracleWhiteList); + const firstSetUnits = new BigNumber(100).mul(firstNaturalUnit).mul(10 ** 18).div(firstSetValue).round(0, 3); + const firstSetCallData = rebalancingSetV3Helper.generateRebalancingSetTokenV3CallData( + deployerAccount, + liquidator.address, + deployerAccount, + ethPerformanceFeeCalculator.address, + ONE_DAY_IN_SECONDS, + ONE_DAY_IN_SECONDS.mul(2), + ZERO, + ZERO, + calculatorData + ); - const rebalancingSetValue1 = await valuationHelper.calculateRebalancingSetTokenValueAsync( - rebalancingSetToken, - ethOracleWhiteList, - ); - const rebalancingSetValue2 = await valuationHelper.calculateRebalancingSetTokenValueAsync( - rebalancingSetToken2, - usdOracleWhiteList, - ); - const expectedStreamingFee1 = await feeCalculatorHelper.calculateAccruedStreamingFee( - feeState1.streamingFeePercentage, - new BigNumber(lastBlock.timestamp).sub(feeState1.lastStreamingFeeTimestamp) - ); - const expectedStreamingFee2 = await feeCalculatorHelper.calculateAccruedStreamingFee( - feeState2.streamingFeePercentage, - new BigNumber(lastBlock.timestamp).sub(feeState2.lastStreamingFeeTimestamp) - ); + rebalancingSetToken = await rebalancingSetV3Helper.createRebalancingTokenV3Async( + coreMock, + rebalancingFactoryV3.address, + [collateralSet.address], + [firstSetUnits], + firstNaturalUnit, + firstSetCallData + ); - const expectedProfitFee1 = await feeCalculatorHelper.calculateAccruedProfitFeeAsync( - feeState1, - rebalancingSetValue1, - new BigNumber(lastBlock.timestamp) - ); - const expectedProfitFee2 = await feeCalculatorHelper.calculateAccruedProfitFeeAsync( - feeState2, - rebalancingSetValue2, - new BigNumber(lastBlock.timestamp) - ); + usdPerformanceFeeCalculator = await feeCalculatorHelper.deployPerformanceFeeCalculatorAsync( + coreMock.address, + usdOracleWhiteList.address, + maxProfitFeePercentage, + maxStreamingFeePercentage + ); + await coreHelper.addAddressToWhiteList(usdPerformanceFeeCalculator.address, feeCalculatorWhitelist); + + const secondNaturalUnit = new BigNumber(10 ** 8); + const secondSetValue = await valuationHelper.calculateSetTokenValueAsync(collateralSet, usdOracleWhiteList); + const secondSetUnits = new BigNumber(100) + .mul(secondNaturalUnit) + .mul(10 ** 18) + .div(secondSetValue) + .round(0, 3); + const secondSetCallData = rebalancingSetV3Helper.generateRebalancingSetTokenV3CallData( + deployerAccount, + liquidator.address, + deployerAccount, + usdPerformanceFeeCalculator.address, + ONE_DAY_IN_SECONDS, + ONE_DAY_IN_SECONDS.mul(2), + ZERO, + ZERO, + calculatorData + ); - const expectedStreamingFeeArray = [expectedStreamingFee1, expectedStreamingFee2]; - const expectedProfitFeeArray = [expectedProfitFee1, expectedProfitFee2]; + secondRebalancingSetToken = await rebalancingSetV3Helper.createRebalancingTokenV3Async( + coreMock, + rebalancingFactoryV3.address, + [collateralSet.address], + [secondSetUnits], + secondNaturalUnit, + secondSetCallData + ); - expect(JSON.stringify(actualStreamingFeeArray)).to.equal(JSON.stringify(expectedStreamingFeeArray)); - expect(JSON.stringify(actualProfitFeeArray)).to.equal(JSON.stringify(expectedProfitFeeArray)); - }); + subjectTradingPools = [rebalancingSetToken.address, secondRebalancingSetToken.address]; }); - describe('#batchFetchTradingPoolFeeState', async () => { - let subjectTradingPools: Address[]; - - let ethOracleWhiteList: OracleWhiteListContract; - let usdOracleWhiteList: OracleWhiteListContract; - - let wrappedETH: StandardTokenMockContract; - let wrappedBTC: StandardTokenMockContract; - let usdc: StandardTokenMockContract; - let dai: StandardTokenMockContract; - - let collateralSet: SetTokenContract; - - let usdWrappedETHOracle: UpdatableOracleMockContract; - let usdWrappedBTCOracle: UpdatableOracleMockContract; - let usdUSDCOracle: UpdatableOracleMockContract; - let usdDaiOracle: UpdatableOracleMockContract; - - let ethWrappedETHOracle: UpdatableOracleMockContract; - let ethWrappedBTCOracle: UpdatableOracleMockContract; - let ethUSDCOracle: UpdatableOracleMockContract; - let ethDaiOracle: UpdatableOracleMockContract; - - let ethPerformanceFeeCalculator: PerformanceFeeCalculatorContract; - let usdPerformanceFeeCalculator: PerformanceFeeCalculatorContract; - - let secondRebalancingSetToken: RebalancingSetTokenV3Contract; - - beforeEach(async () => { - wrappedETH = await erc20Helper.deployTokenAsync(deployerAccount, 18); - wrappedBTC = await erc20Helper.deployTokenAsync(deployerAccount, 8); - usdc = await erc20Helper.deployTokenAsync(deployerAccount, 6); - dai = await erc20Helper.deployTokenAsync(deployerAccount, 18); - - let wrappedETHPrice: BigNumber; - let wrappedBTCPrice: BigNumber; - let usdcPrice: BigNumber; - let daiPrice: BigNumber; - - wrappedETHPrice = ether(128); - wrappedBTCPrice = ether(7500); - usdcPrice = ether(1); - daiPrice = ether(1); - - usdWrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedETHPrice); - usdWrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedBTCPrice); - usdUSDCOracle = await oracleHelper.deployUpdatableOracleMockAsync(usdcPrice); - usdDaiOracle = await oracleHelper.deployUpdatableOracleMockAsync(daiPrice); - - usdOracleWhiteList = await coreHelper.deployOracleWhiteListAsync( - [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], - [usdWrappedETHOracle.address, usdWrappedBTCOracle.address, usdUSDCOracle.address, usdDaiOracle.address], - ); - - ethWrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync( - wrappedETHPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) - ); - ethWrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync( - wrappedBTCPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) - ); - ethUSDCOracle = await oracleHelper.deployUpdatableOracleMockAsync( - usdcPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) - ); - ethDaiOracle = await oracleHelper.deployUpdatableOracleMockAsync( - daiPrice.mul(ether(1)).div(wrappedETHPrice).round(0, 3) - ); - - ethOracleWhiteList = await coreHelper.deployOracleWhiteListAsync( - [wrappedETH.address, wrappedBTC.address, usdc.address, dai.address], - [ethWrappedETHOracle.address, ethWrappedBTCOracle.address, ethUSDCOracle.address, ethDaiOracle.address], - ); - - const maxProfitFeePercentage = ether(.5); - const maxStreamingFeePercentage = ether(.1); - ethPerformanceFeeCalculator = await feeCalculatorHelper.deployPerformanceFeeCalculatorAsync( - coreMock.address, - ethOracleWhiteList.address, - maxProfitFeePercentage, - maxStreamingFeePercentage - ); - await coreHelper.addAddressToWhiteList(ethPerformanceFeeCalculator.address, feeCalculatorWhitelist); - - const collateralSetComponents = [wrappedETH.address, wrappedBTC.address]; - const collateralSetUnits = [wrappedBTCPrice.div(wrappedETHPrice).mul(10 ** 12), new BigNumber(100)]; - const collateralSetNaturalUnit = new BigNumber(10 ** 12); - collateralSet = await coreHelper.createSetTokenAsync( - coreMock, - factory.address, - collateralSetComponents, - collateralSetUnits, - collateralSetNaturalUnit, - ); + async function subject(): Promise { + return protocolViewer.batchFetchTradingPoolFeeState.callAsync( + subjectTradingPools + ); + } - const calculatorData = feeCalculatorHelper.generatePerformanceFeeCallDataBuffer( - ONE_DAY_IN_SECONDS.mul(30), - ONE_YEAR_IN_SECONDS, - ether(.2), - ether(.02) - ); + it('fetches the correct rebalanceFee array', async () => { + const tradingPoolFeeStates = await subject(); - const firstNaturalUnit = new BigNumber(10 ** 8); - const firstSetValue = await valuationHelper.calculateSetTokenValueAsync(collateralSet, usdOracleWhiteList); - const firstSetUnits = new BigNumber(100).mul(firstNaturalUnit).mul(10 ** 18).div(firstSetValue).round(0, 3); - const firstSetCallData = rebalancingSetV3Helper.generateRebalancingSetTokenV3CallData( - deployerAccount, - liquidator.address, - deployerAccount, - ethPerformanceFeeCalculator.address, - ONE_DAY_IN_SECONDS, - ONE_DAY_IN_SECONDS.mul(2), - ZERO, - ZERO, - calculatorData - ); + const firstFeeState: any = await ethPerformanceFeeCalculator.feeState.callAsync(rebalancingSetToken.address); + const secondFeeState: any = await usdPerformanceFeeCalculator.feeState.callAsync( + secondRebalancingSetToken.address + ); - rebalancingSetToken = await rebalancingSetV3Helper.createRebalancingTokenV3Async( - coreMock, - rebalancingFactoryV3.address, - [collateralSet.address], - [firstSetUnits], - firstNaturalUnit, - firstSetCallData - ); + const expectedFeeStateInfo = _.map([firstFeeState, secondFeeState], feeStates => + [ + feeStates.profitFeePeriod, + feeStates.highWatermarkResetPeriod, + feeStates.profitFeePercentage, + feeStates.streamingFeePercentage, + feeStates.highWatermark, + feeStates.lastProfitFeeTimestamp, + feeStates.lastStreamingFeeTimestamp, + ] + ); - usdPerformanceFeeCalculator = await feeCalculatorHelper.deployPerformanceFeeCalculatorAsync( - coreMock.address, - usdOracleWhiteList.address, - maxProfitFeePercentage, - maxStreamingFeePercentage - ); - await coreHelper.addAddressToWhiteList(usdPerformanceFeeCalculator.address, feeCalculatorWhitelist); - - const secondNaturalUnit = new BigNumber(10 ** 8); - const secondSetValue = await valuationHelper.calculateSetTokenValueAsync(collateralSet, usdOracleWhiteList); - const secondSetUnits = new BigNumber(100) - .mul(secondNaturalUnit) - .mul(10 ** 18) - .div(secondSetValue) - .round(0, 3); - const secondSetCallData = rebalancingSetV3Helper.generateRebalancingSetTokenV3CallData( - deployerAccount, - liquidator.address, - deployerAccount, - usdPerformanceFeeCalculator.address, - ONE_DAY_IN_SECONDS, - ONE_DAY_IN_SECONDS.mul(2), - ZERO, - ZERO, - calculatorData - ); + expect(JSON.stringify(tradingPoolFeeStates)).to.equal(JSON.stringify(expectedFeeStateInfo)); + }); + }); + }); - secondRebalancingSetToken = await rebalancingSetV3Helper.createRebalancingTokenV3Async( - coreMock, - rebalancingFactoryV3.address, - [collateralSet.address], - [secondSetUnits], - secondNaturalUnit, - secondSetCallData - ); + describe('#batchFetchOraclePrices', async () => { + let wrappedETHOracle: UpdatableOracleMockContract; + let wrappedBTCOracle: UpdatableOracleMockContract; + let usdcOracle: UpdatableOracleMockContract; + let daiOracle: UpdatableOracleMockContract; - subjectTradingPools = [rebalancingSetToken.address, secondRebalancingSetToken.address]; - }); + let wrappedETHPrice: BigNumber; + let wrappedBTCPrice: BigNumber; + let usdcPrice: BigNumber; + let daiPrice: BigNumber; - async function subject(): Promise { - return protocolViewer.batchFetchTradingPoolFeeState.callAsync( - subjectTradingPools - ); - } + let subjectOracleAddresses: Address[]; - it('fetches the correct rebalanceFee array', async () => { - const tradingPoolFeeStates = await subject(); + beforeEach(async () => { + wrappedETHPrice = ether(128); + wrappedBTCPrice = ether(7500); + usdcPrice = ether(1); + daiPrice = ether(1); + + wrappedETHOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedETHPrice); + wrappedBTCOracle = await oracleHelper.deployUpdatableOracleMockAsync(wrappedBTCPrice); + usdcOracle = await oracleHelper.deployUpdatableOracleMockAsync(usdcPrice); + daiOracle = await oracleHelper.deployUpdatableOracleMockAsync(daiPrice); + + subjectOracleAddresses = [ + wrappedETHOracle.address, + wrappedBTCOracle.address, + usdcOracle.address, + daiOracle.address, + ]; + }); - const firstFeeState: any = await ethPerformanceFeeCalculator.feeState.callAsync(rebalancingSetToken.address); - const secondFeeState: any = await usdPerformanceFeeCalculator.feeState.callAsync( - secondRebalancingSetToken.address - ); + async function subject(): Promise { + return protocolViewer.batchFetchOraclePrices.callAsync( + subjectOracleAddresses, + ); + } - const expectedFeeStateInfo = _.map([firstFeeState, secondFeeState], feeStates => - [ - feeStates.profitFeePeriod, - feeStates.highWatermarkResetPeriod, - feeStates.profitFeePercentage, - feeStates.streamingFeePercentage, - feeStates.highWatermark, - feeStates.lastProfitFeeTimestamp, - feeStates.lastStreamingFeeTimestamp, - ] - ); + it('fetches oracle prices', async () => { + const oraclePrices = await subject(); - expect(JSON.stringify(tradingPoolFeeStates)).to.equal(JSON.stringify(expectedFeeStateInfo)); - }); - }); + const expectedOraclePrices = [wrappedETHPrice, wrappedBTCPrice, usdcPrice, daiPrice]; + expect(JSON.stringify(oraclePrices)).to.equal(JSON.stringify(expectedOraclePrices)); }); }); diff --git a/utils/contracts.ts b/utils/contracts.ts index 48e3aea..ef697cc 100644 --- a/utils/contracts.ts +++ b/utils/contracts.ts @@ -1,9 +1,10 @@ export { BaseContract } from '../types/base_contract'; +export { CTokenViewerContract } from '../types/generated/c_token_viewer'; export { ERC20ViewerContract } from '../types/generated/erc20_viewer'; -export { TrendingManagerMockContract } from '../types/generated/trending_manager_mock'; export { ManagerViewerContract } from '../types/generated/manager_viewer'; +export { OracleViewerContract } from '../types/generated/oracle_viewer'; export { ProtocolViewerContract } from '../types/generated/protocol_viewer'; export { RebalancingSetTokenViewerContract } from '../types/generated/rebalancing_set_token_viewer'; export { SocialTradingManagerMockContract } from '../types/generated/social_trading_manager_mock'; export { TradingPoolViewerContract } from '../types/generated/trading_pool_viewer'; -export { CTokenViewerContract } from '../types/generated/c_token_viewer'; \ No newline at end of file +export { TrendingManagerMockContract } from '../types/generated/trending_manager_mock'; \ No newline at end of file diff --git a/utils/helpers/protocolViewerHelper.ts b/utils/helpers/protocolViewerHelper.ts index 02fcecb..ccbefef 100644 --- a/utils/helpers/protocolViewerHelper.ts +++ b/utils/helpers/protocolViewerHelper.ts @@ -5,11 +5,12 @@ import { CTokenViewerContract, ERC20ViewerContract, ManagerViewerContract, - TrendingManagerMockContract, + OracleViewerContract, ProtocolViewerContract, RebalancingSetTokenViewerContract, SocialTradingManagerMockContract, TradingPoolViewerContract, + TrendingManagerMockContract, } from '../contracts'; import { getContractInstance, txnFrom } from '../web3Helper'; import { @@ -35,11 +36,12 @@ const Bytes32Library = const CTokenViewer = artifacts.require('CTokenViewer'); const ERC20Viewer = artifacts.require('ERC20Viewer'); const ManagerViewer = artifacts.require('ManagerViewer'); -const TrendingManagerMock = artifacts.require('TrendingManagerMock'); +const OracleViewer = artifacts.require('OracleViewer'); const ProtocolViewer = artifacts.require('ProtocolViewer'); const RebalancingSetTokenViewer = artifacts.require('RebalancingSetTokenViewer'); const SocialTradingManagerMock = artifacts.require('SocialTradingManagerMock'); const TradingPoolViewer = artifacts.require('TradingPoolViewer'); +const TrendingManagerMock = artifacts.require('TrendingManagerMock'); const contract = require('truffle-contract'); @@ -91,6 +93,20 @@ export class ProtocolViewerHelper { ); } + public async deployOracleViewerAsync( + from: Address = this._contractOwnerAddress + ): Promise { + const + oracleViewer = await OracleViewer.new( + txnFrom(from) + ); + + return new OracleViewerContract( + getContractInstance(oracleViewer), + txnFrom(from), + ); + } + public async deployProtocolViewerAsync( from: Address = this._contractOwnerAddress ): Promise {