import * as _ from 'lodash' ; import * as ethUtil from 'ethereumjs-util' ; import * as setProtocolUtils from 'set-protocol-utils' ; import { Address , Bytes } from 'set-protocol-utils' ; import { ether } from '@utils/units' ; import { SetTokenContract } from 'set-protocol-contracts' ; import { MedianContract , MovingAverageOracleContract , MovingAverageOracleV2Contract } from 'set-protocol-oracles' ; import { AssetPairManagerContract , AssetPairManagerV2Contract , BinaryAllocatorContract , BinaryAllocatorMockContract , InverseMACOStrategyManagerContract , MACOStrategyManagerContract , MACOStrategyManagerV2Contract , MovingAverageCrossoverTriggerContract , RSITrendingTriggerContract , SocialTradingManagerContract , SocialTradingManagerV2Contract , TriggerMockContract , TwoMovingAverageCrossoverTriggerContract , SocialAllocatorContract , } from '../contracts' ; import { BigNumber } from 'bignumber.js' ; import { DEFAULT_GAS , DEFAULT_REBALANCING_NATURAL_UNIT , ETH_DECIMALS , ONE_DAY_IN_SECONDS , ONE_HOUR_IN_SECONDS , USDC_DECIMALS , VALUE_TO_CENTS_CONVERSION , ZERO , ZERO_BYTES } from '../constants' ; import { extractNewCollateralFromLogs } from '@utils/contract_logs/binaryAllocator' ; import { ProtocolHelper } from '@utils/helpers/protocolHelper' ; import { getWeb3 , getContractInstance , importArtifactsFromSource } from '../web3Helper' ; const web3 = getWeb3 ( ) ; const AssetPairManager = importArtifactsFromSource ( 'AssetPairManager' ) ; const AssetPairManagerV2 = importArtifactsFromSource ( 'AssetPairManagerV2' ) ; const BinaryAllocator = importArtifactsFromSource ( 'BinaryAllocator' ) ; const BinaryAllocatorMock = importArtifactsFromSource ( 'BinaryAllocatorMock' ) ; const InverseMACOStrategyManager = importArtifactsFromSource ( 'InverseMACOStrategyManager' ) ; const MACOStrategyManager = importArtifactsFromSource ( 'MACOStrategyManager' ) ; const MACOStrategyManagerV2 = importArtifactsFromSource ( 'MACOStrategyManagerV2' ) ; const MovingAverageCrossoverTrigger = importArtifactsFromSource ( 'MovingAverageCrossoverTrigger' ) ; const RSITrendingTrigger = importArtifactsFromSource ( 'RSITrendingTrigger' ) ; const SocialTradingManager = importArtifactsFromSource ( 'SocialTradingManager' ) ; const SocialTradingManagerV2 = importArtifactsFromSource ( 'SocialTradingManagerV2' ) ; const TriggerMock = importArtifactsFromSource ( 'TriggerMock' ) ; const UintArrayUtilsLibrary = importArtifactsFromSource ( 'UintArrayUtilsLibrary' ) ; const SocialAllocator = importArtifactsFromSource ( 'SocialAllocator' ) ; const TwoMovingAverageCrossoverTrigger = importArtifactsFromSource ( 'TwoMovingAverageCrossoverTrigger' ) ; const { SetProtocolUtils : SetUtils , SetProtocolTestUtils : SetTestUtils } = setProtocolUtils ; const setTestUtils = new SetTestUtils ( web3 ) ; const { SET_FULL_TOKEN_UNITS } = SetUtils . CONSTANTS ; export class ManagerHelper { private _tokenOwnerAddress : Address ; constructor ( tokenOwnerAddress ) { this . _tokenOwnerAddress = tokenOwnerAddress ; } public async deployMACOStrategyManagerAsync ( coreAddress , movingAveragePriceFeedAddress , daiAddress , ethAddress , stableCollateralAddress , riskCollateralAddress , setTokenFactoryAddress , auctionLibrary , movingAverageDays , crossoverConfirmationBounds , auctionTimeToPivot = new BigNumber ( 100000 ) , from = this . _tokenOwnerAddress ) { const truffleRebalacingTokenManager = await MACOStrategyManager . new ( coreAddress , movingAveragePriceFeedAddress , daiAddress , ethAddress , stableCollateralAddress , riskCollateralAddress , setTokenFactoryAddress , auctionLibrary , movingAverageDays , auctionTimeToPivot , crossoverConfirmationBounds , { from } ) ; return new MACOStrategyManagerContract ( getContractInstance ( truffleRebalacingTokenManager ) , { from , gas : DEFAULT_GAS } ) ; } public async deployMACOStrategyManagerV2Async ( coreAddress , movingAveragePriceFeedAddress , riskAssetOracleAddress , daiAddress , ethAddress , stableCollateralAddress , riskCollateralAddress , setTokenFactoryAddress , auctionLibrary , movingAverageDays , crossoverConfirmationBounds , auctionTimeToPivot = new BigNumber ( 100000 ) , from = this . _tokenOwnerAddress ) { const truffleRebalacingTokenManager = await MACOStrategyManagerV2 . new ( coreAddress , movingAveragePriceFeedAddress , riskAssetOracleAddress , daiAddress , ethAddress , [ stableCollateralAddress , riskCollateralAddress ] , setTokenFactoryAddress , auctionLibrary , movingAverageDays , auctionTimeToPivot , crossoverConfirmationBounds , { from } ) ; return new MACOStrategyManagerV2Contract ( getContractInstance ( truffleRebalacingTokenManager ) , { from , gas : DEFAULT_GAS } ) ; } public async deployInverseMACOStrategyManagerAsync ( coreAddress , movingAveragePriceFeedAddress , riskAssetOracleAddress , daiAddress , ethAddress , stableCollateralAddress , riskCollateralAddress , setTokenFactoryAddress , auctionLibrary , movingAverageDays , crossoverConfirmationBounds , auctionTimeToPivot = new BigNumber ( 100000 ) , from = this . _tokenOwnerAddress ) { const truffleRebalacingTokenManager = await InverseMACOStrategyManager . new ( coreAddress , movingAveragePriceFeedAddress , riskAssetOracleAddress , daiAddress , ethAddress , [ stableCollateralAddress , riskCollateralAddress ] , setTokenFactoryAddress , auctionLibrary , movingAverageDays , auctionTimeToPivot , crossoverConfirmationBounds , { from } ) ; return new InverseMACOStrategyManagerContract ( getContractInstance ( truffleRebalacingTokenManager ) , { from , gas : DEFAULT_GAS } ) ; } public async deployAssetPairManagerAsync ( coreInstance , allocatorInstance , triggerInstance , auctionLibraryInstance , baseAssetAllocation , allocationPrecision = new BigNumber ( 100 ) , maxBaseAssetAllocation = new BigNumber ( 100 ) , auctionStartPercentage = new BigNumber ( 2 ) , auctionEndPercentage = new BigNumber ( 10 ) , auctionTimeToPivot = ONE_HOUR_IN_SECONDS . mul ( 4 ) , signalConfirmationMinTime = ONE_HOUR_IN_SECONDS . mul ( 6 ) , signalConfirmationMaxTime = ONE_HOUR_IN_SECONDS . mul ( 12 ) , from = this . _tokenOwnerAddress ) { const truffleRebalacingTokenManager = await AssetPairManager . new ( coreInstance , allocatorInstance , triggerInstance , auctionLibraryInstance , baseAssetAllocation , allocationPrecision , maxBaseAssetAllocation , auctionTimeToPivot , [ auctionStartPercentage , auctionEndPercentage ] , [ signalConfirmationMinTime , signalConfirmationMaxTime ] , { from } ) ; return new AssetPairManagerContract ( getContractInstance ( truffleRebalacingTokenManager ) , { from , gas : DEFAULT_GAS } ) ; } public async deployAssetPairManagerV2Async ( coreInstance , allocatorInstance , triggerInstance , useBullishAllocation , allocationPrecision = new BigNumber ( 100 ) , maxBaseAssetAllocation = new BigNumber ( 100 ) , signalConfirmationMinTime = ONE_HOUR_IN_SECONDS . mul ( 6 ) , signalConfirmationMaxTime = ONE_HOUR_IN_SECONDS . mul ( 12 ) , liquidatorData = ZERO_BYTES , from = this . _tokenOwnerAddress ) { const truffleRebalacingTokenManager = await AssetPairManagerV2 . new ( coreInstance , allocatorInstance , triggerInstance , useBullishAllocation , allocationPrecision , maxBaseAssetAllocation , [ signalConfirmationMinTime , signalConfirmationMaxTime ] , liquidatorData , { from } ) ; return new AssetPairManagerV2Contract ( getContractInstance ( truffleRebalacingTokenManager ) , { from , gas : DEFAULT_GAS } ) ; } public async deploySocialTradingManagerAsync ( coreInstance , factory , whiteListedAllocators , maxEntryFee = ether ( .1 ) , feeUpdateTimelock = ONE_DAY_IN_SECONDS , from = this . _tokenOwnerAddress ) { const truffleRebalacingTokenManager = await SocialTradingManager . new ( coreInstance , factory , whiteListedAllocators , maxEntryFee , feeUpdateTimelock , { from } ) ; return new SocialTradingManagerContract ( getContractInstance ( truffleRebalacingTokenManager ) , { from , gas : DEFAULT_GAS } ) ; } public async deploySocialTradingManagerV2Async ( coreInstance , factory , whiteListedAllocators , maxEntryFee = ether ( .1 ) , feeUpdateTimelock = ONE_DAY_IN_SECONDS , from = this . _tokenOwnerAddress ) { const truffleRebalacingTokenManager = await SocialTradingManagerV2 . new ( coreInstance , factory , whiteListedAllocators , maxEntryFee , feeUpdateTimelock , { from } ) ; return new SocialTradingManagerV2Contract ( getContractInstance ( truffleRebalacingTokenManager ) , { from , gas : DEFAULT_GAS } ) ; } public async deployTriggerMocksAsync ( priceTriggerCount , initialStates ) { const priceTriggers = [ ] ; const priceTriggersPromises = _ . times ( priceTriggerCount , async ( index ) => { return await TriggerMock . new ( initialStates [ index ] , { from : this . _tokenOwnerAddress , gas : DEFAULT_GAS } ) ; } ) ; await Promise . all ( priceTriggersPromises ) . then ( priceTriggerInstances => { _ . each ( priceTriggerInstances , priceTrigger => { priceTriggers . push ( new TriggerMockContract ( getContractInstance ( priceTrigger ) , { from : this . _tokenOwnerAddress , gas : DEFAULT_GAS } ) ) ; } ) ; } ) ; return priceTriggers ; } public async deployMovingAverageCrossoverTrigger ( movingAveragePriceFeed , assetPairOracle , movingAverageDays , from = this . _tokenOwnerAddress ) { const trufflePriceTrigger = await MovingAverageCrossoverTrigger . new ( movingAveragePriceFeed , assetPairOracle , movingAverageDays , { from } ) ; return new MovingAverageCrossoverTriggerContract ( getContractInstance ( trufflePriceTrigger ) , { from , gas : DEFAULT_GAS } ) ; } public async deployTwoMovingAverageCrossoverTrigger ( longTermMAOracle , shortTermMAOracle , longTermMADays , shortTermMADays , from = this . _tokenOwnerAddress ) { const trufflePriceTrigger = await TwoMovingAverageCrossoverTrigger . new ( longTermMAOracle , shortTermMAOracle , longTermMADays , shortTermMADays , { from } ) ; return new TwoMovingAverageCrossoverTriggerContract ( getContractInstance ( trufflePriceTrigger ) , { from , gas : DEFAULT_GAS } ) ; } public async deployRSITrendingTrigger ( rsiOracleInstance , lowerBound , upperBound , rsiTimePeriod , from = this . _tokenOwnerAddress ) { const trufflePriceTrigger = await RSITrendingTrigger . new ( rsiOracleInstance , lowerBound , upperBound , rsiTimePeriod , { from } ) ; return new RSITrendingTriggerContract ( getContractInstance ( trufflePriceTrigger ) , { from , gas : DEFAULT_GAS } ) ; } public async deployBinaryAllocatorAsync ( baseAssetInstance , quoteAssetInstance , baseAssetOracleInstance , quoteAssetOracleInstance , baseAssetCollateralInstance , quoteAssetCollateralInstance , coreInstance , setTokenFactoryAddress , from = this . _tokenOwnerAddress ) { const truffleAllocationPricer = await BinaryAllocator . new ( baseAssetInstance , quoteAssetInstance , baseAssetOracleInstance , quoteAssetOracleInstance , baseAssetCollateralInstance , quoteAssetCollateralInstance , coreInstance , setTokenFactoryAddress , { from } ) ; return new BinaryAllocatorContract ( getContractInstance ( truffleAllocationPricer ) , { from , gas : DEFAULT_GAS } ) ; } public async deployBinaryAllocatorMockAsync ( baseAssetCollateralInstance , quoteAssetCollateralInstance , baseAssetCollateralValue , quoteAssetCollateralValue , from = this . _tokenOwnerAddress ) { const truffleAllocationPricer = await BinaryAllocatorMock . new ( baseAssetCollateralInstance , quoteAssetCollateralInstance , baseAssetCollateralValue , quoteAssetCollateralValue , { from } ) ; return new BinaryAllocatorMockContract ( getContractInstance ( truffleAllocationPricer ) , { from , gas : DEFAULT_GAS } ) ; } public async deploySocialAllocatorAsync ( baseAssetInstance , quoteAssetInstance , oracleWhiteListInstance , coreInstance , setTokenFactoryAddress , pricePrecision = new BigNumber ( 100 ) , collateralName = SetUtils . stringToBytes ( 'CollateralName' ) , collateralSymbol = SetUtils . stringToBytes ( 'COL' ) , from = this . _tokenOwnerAddress ) { const truffleAllocationPricer = await SocialAllocator . new ( baseAssetInstance , quoteAssetInstance , oracleWhiteListInstance , coreInstance , setTokenFactoryAddress , pricePrecision , collateralName , collateralSymbol , { from } ) ; return new SocialAllocatorContract ( getContractInstance ( truffleAllocationPricer ) , { from , gas : DEFAULT_GAS } ) ; } public async linkUintArrayUtilsLibraryAsync ( contract ) { const truffleUintArrayUtilsLibrary = await UintArrayUtilsLibrary . new ( { from : this . _tokenOwnerAddress } ) ; await contract . link ( 'UintArrayUtilsLibrary' , truffleUintArrayUtilsLibrary . address ) ; } public async getNewBinaryAllocatorCollateralFromLogs ( txHash , protocolHelper ) { const logs = await setTestUtils . getLogsFromTxHash ( txHash ) ; const [ , nextSetAddress ] = extractNewCollateralFromLogs ( [ logs [ 1 ] ] ) ; return await protocolHelper . getSetTokenAsync ( nextSetAddress ) ; } public calculateCollateralSetHash ( units , naturalUnit , component ) { const hexString = SetTestUtils . bufferArrayToHex ( [ SetUtils . paddedBufferForBigNumber ( units ) , SetUtils . paddedBufferForBigNumber ( naturalUnit ) , ethUtil . toBuffer ( component ) , ] ) ; return web3 . utils . soliditySha3 ( hexString ) ; } public async getMACOInitialAllocationAsync ( stableCollateral , riskCollateral , spotPriceOracle , movingAverageOracle , dataDays ) { const spotPrice = parseInt ( await spotPriceOracle . read . callAsync ( ) ) ; const rawMAPrice = await movingAverageOracle . read . callAsync ( dataDays ) ; const maPriceNum = parseInt ( rawMAPrice . toString ( ) ) ; if ( spotPrice > maPriceNum ) { return riskCollateral . address ; } else { return stableCollateral . address ; } } public async getInverseMACOInitialAllocationAsync ( stableCollateral , riskCollateral , spotPriceOracle , movingAverageOracle , dataDays ) { const spotPrice = parseInt ( await spotPriceOracle . read . callAsync ( ) ) ; const rawMAPrice = await movingAverageOracle . read . callAsync ( dataDays ) ; const maPriceNum = parseInt ( rawMAPrice . toString ( ) ) ; if ( spotPrice > maPriceNum ) { return stableCollateral . address ; } else { return riskCollateral . address ; } } public getExpectedGeneralNextSetParameters ( tokenOnePrice , tokenTwoPrice , tokenOneMultiplier , tokenTwoMultiplier , decimalDifference , pricePrecision ) { let units ; const naturalUnit = pricePrecision . mul ( decimalDifference ) ; if ( tokenTwoPrice . greaterThanOrEqualTo ( tokenOnePrice ) ) { const tokenOneUnits = tokenTwoPrice . mul ( decimalDifference ) . mul ( pricePrecision ) . div ( tokenOnePrice ) . round ( 0 , 3 ) ; units = [ tokenOneMultiplier . mul ( tokenOneUnits ) , tokenTwoMultiplier . mul ( pricePrecision ) ] ; } else { const tokenTwoUnits = tokenOnePrice . mul ( pricePrecision ) . div ( tokenTwoPrice ) . round ( 0 , 3 ) ; units = [ pricePrecision . mul ( decimalDifference ) . mul ( tokenOneMultiplier ) , tokenTwoUnits . mul ( tokenTwoMultiplier ) ] ; } units = units . filter ( unit => unit . greaterThan ( 0 ) ) ; return { units , naturalUnit , } ; } public async getExpectedNewBinaryAllocationParametersAsync ( currentCollateralSet , currentAssetPrice , nextAssetPrice , currentAssetDecimals , nextAssetDecimals ) { let naturalUnit ; let units ; const currentUnits = await currentCollateralSet . getUnits . callAsync ( ) ; const currentNaturalUnit = await currentCollateralSet . naturalUnit . callAsync ( ) ; const currentCollateralUSDValue = this . computeTokenDollarAmount ( currentAssetPrice , SET_FULL_TOKEN_UNITS . mul ( currentUnits ) . div ( currentNaturalUnit ) , currentAssetDecimals ) ; let newUnits = ZERO ; let naturalUnitMultiplier = new BigNumber ( 1 ) ; const minimumNaturalUnit = BigNumber . max ( DEFAULT_REBALANCING_NATURAL_UNIT , new BigNumber ( 10 ** 18 ) . div ( nextAssetDecimals ) ) ; while ( newUnits . lessThan ( 1 ) ) { naturalUnit = minimumNaturalUnit . mul ( naturalUnitMultiplier ) ; newUnits = this . calculateNewUnits ( currentCollateralUSDValue , nextAssetPrice , nextAssetDecimals , naturalUnit ) ; naturalUnitMultiplier = naturalUnitMultiplier . mul ( 10 ) ; } units = [ new BigNumber ( this . roundToNearestPowerOfTwo ( newUnits . toNumber ( ) ) ) ] ; return { units , naturalUnit , } ; } public async getExpectedMACONewCollateralParametersAsync ( stableCollateral , riskCollateral , spotPriceOracle , stableCollateralDecimals , riskCollateralDecimals , riskOn ) { let naturalUnit ; let units ; const currentEthPrice = new BigNumber ( await spotPriceOracle . read . callAsync ( ) ) ; const currentUSDCPrice = new BigNumber ( 10 ** 18 ) ; const riskUnits = await riskCollateral . getUnits . callAsync ( ) ; const riskNaturalUnit = await riskCollateral . naturalUnit . callAsync ( ) ; const stableUnits = await stableCollateral . getUnits . callAsync ( ) ; const stableNaturalUnit = await stableCollateral . naturalUnit . callAsync ( ) ; let newUnits = new BigNumber ( 1 ) ; let naturalUnitMultiplier = new BigNumber ( 1 ) ; if ( riskOn ) { const riskCollateralUSDValue = this . computeTokenDollarAmount ( currentEthPrice , SET_FULL_TOKEN_UNITS . mul ( riskUnits ) . div ( riskNaturalUnit ) , riskCollateralDecimals ) ; while ( newUnits . lessThanOrEqualTo ( 1 ) ) { naturalUnit = stableNaturalUnit . mul ( naturalUnitMultiplier ) ; newUnits = this . calculateNewUnits ( riskCollateralUSDValue , currentUSDCPrice , USDC_DECIMALS , naturalUnit ) ; naturalUnitMultiplier = naturalUnitMultiplier . mul ( 10 ) ; } units = [ newUnits ] ; } else { const stableCollateralUSDValue = this . computeTokenDollarAmount ( currentUSDCPrice , SET_FULL_TOKEN_UNITS . mul ( stableUnits ) . div ( stableNaturalUnit ) , stableCollateralDecimals ) ; while ( newUnits . lessThanOrEqualTo ( 1 ) ) { naturalUnit = riskNaturalUnit . mul ( naturalUnitMultiplier ) ; newUnits = this . calculateNewUnits ( stableCollateralUSDValue , currentEthPrice , ETH_DECIMALS , naturalUnit ) ; naturalUnitMultiplier = naturalUnitMultiplier . mul ( 10 ) ; } } units = [ newUnits ] ; return { units , naturalUnit , } ; } private calculateNewUnits ( currentUSDValue , replacedCollateralPrice , replacedCollateralDecimals , replacedCollateralNaturalUnit ) { return currentUSDValue . mul ( replacedCollateralDecimals ) . mul ( replacedCollateralNaturalUnit ) . div ( SET_FULL_TOKEN_UNITS ) . round ( 0 , 3 ) . div ( replacedCollateralPrice . div ( VALUE_TO_CENTS_CONVERSION ) ) . round ( 0 , 3 ) ; } public async getExpectedGeneralAuctionParameters ( tokenOnePrice , tokenTwoPrice , tokenOneMultiplier , tokenTwoMultiplier , tokenOneDecimals , tokenTwoDecimals , pricePrecision , auctionTimeToPivot , currentSetToken ) { const THIRTY_MINUTES_IN_SECONDS = new BigNumber ( 30 * 60 ) ; const nextSetParams = this . getExpectedGeneralNextSetParameters ( tokenOnePrice , tokenTwoPrice , tokenOneMultiplier , tokenTwoMultiplier , tokenOneDecimals . div ( tokenTwoDecimals ) , pricePrecision ) ; const currentSetNaturalUnit = await currentSetToken . naturalUnit . callAsync ( ) ; const currentSetUnits = await currentSetToken . getUnits . callAsync ( ) ; const currentSetDollarAmount = this . computeTokenValue ( currentSetUnits , currentSetNaturalUnit , tokenOnePrice , tokenTwoPrice , tokenOneDecimals , tokenTwoDecimals ) ; const nextSetDollarAmount = this . computeTokenValue ( nextSetParams [ 'units' ] , nextSetParams [ 'naturalUnit' ] , tokenOnePrice , tokenTwoPrice , tokenOneDecimals , tokenTwoDecimals ) ; const fairValue = nextSetDollarAmount . div ( currentSetDollarAmount ) . mul ( 1000 ) . round ( 0 , 3 ) ; const onePercentSlippage = fairValue . div ( 100 ) . round ( 0 , 3 ) ; const thirtyMinutePeriods = auctionTimeToPivot . div ( THIRTY_MINUTES_IN_SECONDS ) . round ( 0 , 3 ) ; const halfPriceRange = thirtyMinutePeriods . mul ( onePercentSlippage ) . div ( 2 ) . round ( 0 , 3 ) ; const auctionStartPrice = fairValue . sub ( halfPriceRange ) ; const auctionPivotPrice = fairValue . add ( halfPriceRange ) ; return { auctionStartPrice , auctionPivotPrice , } ; } public async getExpectedMACOAuctionParametersAsync ( currentSetToken , nextSetToken , riskOn , ethPrice , timeIncrement , auctionTimeToPivot ) { let nextSetDollarAmount ; let currentSetDollarAmount ; const nextSetUnits = await nextSetToken . getUnits . callAsync ( ) ; const nextSetNaturalUnit = await nextSetToken . naturalUnit . callAsync ( ) ; const currentSetUnits = await currentSetToken . getUnits . callAsync ( ) ; const currentSetNaturalUnit = await currentSetToken . naturalUnit . callAsync ( ) ; const USDC_PRICE = new BigNumber ( 10 ** 18 ) ; if ( riskOn ) { nextSetDollarAmount = nextSetUnits [ 0 ] . mul ( SET_FULL_TOKEN_UNITS ) . mul ( USDC_PRICE ) . div ( nextSetNaturalUnit ) . div ( USDC_DECIMALS ) ; currentSetDollarAmount = currentSetUnits [ 0 ] . mul ( SET_FULL_TOKEN_UNITS ) . mul ( ethPrice ) . div ( currentSetNaturalUnit ) . div ( ETH_DECIMALS ) ; } else { currentSetDollarAmount = currentSetUnits [ 0 ] . mul ( SET_FULL_TOKEN_UNITS ) . mul ( USDC_PRICE ) . div ( currentSetNaturalUnit ) . div ( USDC_DECIMALS ) ; nextSetDollarAmount = nextSetUnits [ 0 ] . mul ( SET_FULL_TOKEN_UNITS ) . mul ( ethPrice ) . div ( nextSetNaturalUnit ) . div ( ETH_DECIMALS ) ; } return this . calculateLinearAuctionParameters ( currentSetDollarAmount , nextSetDollarAmount , auctionTimeToPivot . div ( timeIncrement ) . div ( 2 ) , auctionTimeToPivot . div ( timeIncrement ) . div ( 2 ) ) ; } public calculateLinearAuctionParameters ( currentSetValue , nextSetValue , auctionStartPercentage , auctionEndPercentage ) { const fairValue = nextSetValue . div ( currentSetValue ) . mul ( 1000 ) . round ( 0 , 3 ) ; const onePercentSlippage = fairValue . div ( 100 ) . round ( 0 , 3 ) ; const auctionStartPrice = fairValue . sub ( auctionStartPercentage . mul ( onePercentSlippage ) ) ; const auctionPivotPrice = fairValue . add ( auctionEndPercentage . mul ( onePercentSlippage ) ) ; return { auctionStartPrice , auctionPivotPrice , } ; } public async calculateSetTokenValue ( setToken , componentPrices , componentDecimals ) { const collateralSetUnits = await setToken . getUnits . callAsync ( ) ; const collateralSetNaturalUnit = await setToken . naturalUnit . callAsync ( ) ; let collateralSetValue = new BigNumber ( 0 ) ; for ( let i = 0 ; i < collateralSetUnits . length ; i ++ ) { const componentUnitsInFullToken = SET_FULL_TOKEN_UNITS . mul ( collateralSetUnits [ i ] ) . div ( collateralSetNaturalUnit ) . round ( 0 , 3 ) ; const componentDollarAmount = componentPrices [ i ] . mul ( componentUnitsInFullToken ) . div ( componentDecimals [ i ] ) ; collateralSetValue = collateralSetValue . add ( componentDollarAmount ) ; } return collateralSetValue ; } private computeTokenValue ( units , naturalUnit , tokenOnePrice , tokenTwoPrice , tokenOneDecimals , tokenTwoDecimals ) { const tokenOneUnitsInFullToken = SET_FULL_TOKEN_UNITS . mul ( units [ 0 ] ) . div ( naturalUnit ) . round ( 0 , 3 ) ; const tokenTwoUnitsInFullToken = SET_FULL_TOKEN_UNITS . mul ( units [ 1 ] ) . div ( naturalUnit ) . round ( 0 , 3 ) ; const tokenOneDollarAmount = this . computeTokenDollarAmount ( tokenOnePrice , tokenOneUnitsInFullToken , tokenOneDecimals ) ; const tokenTwoDollarAmount = this . computeTokenDollarAmount ( tokenTwoPrice , tokenTwoUnitsInFullToken , tokenTwoDecimals ) ; return tokenOneDollarAmount . add ( tokenTwoDollarAmount ) ; } private computeTokenDollarAmount ( tokenPrice , unitsInFullSet , tokenDecimals ) { return tokenPrice . mul ( unitsInFullSet ) . div ( tokenDecimals ) . div ( VALUE_TO_CENTS_CONVERSION ) . round ( 0 , 3 ) ; } public roundToNearestPowerOfTwo ( value ) { let scaledValue = value * 3 / 2 ; let power = 0 ; if ( scaledValue >= 0x100000000000000000000000000000000 ) { scaledValue /= 2 ** 128 ; power += 128 ; } if ( scaledValue >= 0x10000000000000000 ) { scaledValue /= 2 ** 64 ; power += 64 ; } if ( scaledValue >= 0x100000000 ) { scaledValue /= 2 ** 32 ; power += 32 ; } if ( scaledValue >= 0x10000 ) { scaledValue /= 2 ** 16 ; power += 16 ; } if ( scaledValue >= 0x100 ) { scaledValue /= 2 ** 8 ; power += 8 ; } if ( scaledValue >= 0x10 ) { scaledValue /= 2 ** 4 ; power += 4 ; } if ( scaledValue >= 0x4 ) { scaledValue /= 2 ** 2 ; power += 2 ; } if ( scaledValue >= 0x2 ) power += 1 ; return 2 ** power ; } public ceilLog10 ( value ) { const valueNum = value . toNumber ( ) ; if ( valueNum == 1 ) return ZERO ; let x = valueNum - 1 ; let result = 0 ; if ( x >= 10000000000000000000000000000000000000000000000000000000000000000 ) { x /= 10000000000000000000000000000000000000000000000000000000000000000 ; result += 64 ; } if ( x >= 100000000000000000000000000000000 ) { x /= 100000000000000000000000000000000 ; result += 32 ; } if ( x >= 10000000000000000 ) { x /= 10000000000000000 ; result += 16 ; } if ( x >= 100000000 ) { x /= 100000000 ; result += 8 ; } if ( x >= 10000 ) { x /= 10000 ; result += 4 ; } if ( x >= 100 ) { x /= 100 ; result += 2 ; } if ( x >= 10 ) { x /= 10 ; result += 1 ; } return new BigNumber ( result + 1 ) ; } }