@@ -14,7 +14,7 @@ import { InfoTooltip } from 'ts/components/ui/info_tooltip';
1414import { useAPIClient } from 'ts/hooks/use_api_client' ;
1515
1616import { State } from 'ts/redux/reducer' ;
17- import { PoolEpochRewards , PoolWithHistoricalStats , WebsitePaths } from 'ts/types' ;
17+ import { EpochPoolStats , PoolEpochRewards , PoolWithHistoricalStats , WebsitePaths } from 'ts/types' ;
1818import { errorReporter } from 'ts/utils/error_reporter' ;
1919import { formatEther , formatPercent , formatZrx } from 'ts/utils/format_number' ;
2020import { stakingUtils } from 'ts/utils/staking_utils' ;
@@ -61,34 +61,39 @@ export const StakingPool: React.FC<StakingPoolProps & RouteChildrenProps> = (pro
6161 const apiClient = useAPIClient ( networkId ) ;
6262 const [ stakingPool , setStakingPool ] = useState < PoolWithHistoricalStats | undefined > ( undefined ) ;
6363 const [ epochRewards , setEpochRewards ] = useState < PoolEpochRewards [ ] | undefined > ( undefined ) ;
64- const [ stakingPoolAPY , setStakingPoolAPY ] = useState < number | undefined > ( undefined ) ;
64+ const [ stakingPoolAPY3Epochs , setStakingPoolAPY3Epochs ] = useState < number | undefined > ( undefined ) ;
65+ const [ stakingPoolAPY12Epochs , setStakingPoolAPY12Epochs ] = useState < number | undefined > ( undefined ) ;
6566
6667 useEffect ( ( ) => {
67- const calcAverageEpochApy = ( numEpochs : number , rewards : PoolEpochRewards [ ] ) => {
68- const rewardsToAverage =
69- rewards . length > numEpochs ? rewards . slice ( Math . max ( rewards . length - numEpochs , 0 ) ) : rewards ;
68+ const calcShortTermAndLongTermAPY = ( rewards : PoolEpochRewards [ ] ) => {
69+ const average = ( arr : number [ ] ) => arr . reduce ( ( sum , el ) => sum + el , 0 ) / arr . length ;
7070
71- const historicalAPYs = rewardsToAverage . map ( ( reward ) => {
71+ const epochRewardAPYs = rewards . map ( ( reward ) => {
7272 return reward . apy ;
7373 } ) ;
74- const average = ( arr : number [ ] ) => arr . reduce ( ( sum , el ) => sum + el , 0 ) / arr . length ;
75- setStakingPoolAPY ( average ( historicalAPYs ) ) ;
74+ const rewardsToAverageShortTerm =
75+ epochRewardAPYs . length > 3 ? epochRewardAPYs . slice ( Math . max ( rewards . length - 3 , 0 ) ) : epochRewardAPYs ;
76+
77+ const rewardsToAverageLongTerm =
78+ epochRewardAPYs . length > 12 ? epochRewardAPYs . slice ( Math . max ( rewards . length - 12 , 0 ) ) : epochRewardAPYs ;
79+
80+ setStakingPoolAPY3Epochs ( average ( rewardsToAverageShortTerm ) ) ;
81+ setStakingPoolAPY12Epochs ( average ( rewardsToAverageLongTerm ) ) ;
7682 } ;
7783 apiClient
7884 . getStakingPoolByIdAsync ( poolId )
7985 . then ( ( res ) => {
8086 setStakingPool ( res . stakingPool ) ;
81- apiClient
82- . getStakingPoolRewardsAsync ( poolId )
83- . then ( ( resRewards ) => {
84- // fix this, shouldnt be unnecessarily nested
85- setEpochRewards ( resRewards . stakingPoolRewards . epochRewards ) ;
86- calcAverageEpochApy ( 7 , resRewards . stakingPoolRewards . epochRewards ) ;
87- } )
88- . catch ( ( err : Error ) => {
89- logUtils . warn ( err ) ;
90- errorReporter . report ( err ) ;
91- } ) ;
87+ } )
88+ . catch ( ( err : Error ) => {
89+ logUtils . warn ( err ) ;
90+ errorReporter . report ( err ) ;
91+ } ) ;
92+ apiClient
93+ . getStakingPoolRewardsAsync ( poolId )
94+ . then ( ( resRewards ) => {
95+ setEpochRewards ( resRewards . stakingPoolRewards . epochRewards ) ;
96+ calcShortTermAndLongTermAPY ( resRewards . stakingPoolRewards . epochRewards ) ;
9297 } )
9398 . catch ( ( err : Error ) => {
9499 logUtils . warn ( err ) ;
@@ -101,143 +106,148 @@ export const StakingPool: React.FC<StakingPoolProps & RouteChildrenProps> = (pro
101106 return < Redirect to = { WebsitePaths . Staking } /> ;
102107 }
103108
104- if ( ! stakingPool ) {
105- return null ;
106- }
107-
108- const currentEpoch = stakingPool . currentEpochStats ;
109- const nextEpoch = stakingPool . nextEpochStats ;
109+ let currentEpoch : EpochPoolStats ,
110+ nextEpoch ,
111+ historicalEpochs ,
112+ zrxStakeChangeBetweenEpochs : number ,
113+ zrxToStaked = null ;
114+ if ( stakingPool ) {
115+ currentEpoch = stakingPool . currentEpochStats ;
116+ nextEpoch = stakingPool . nextEpochStats ;
110117
111- // Reminder: stake change can be negative
112- const zrxStakeChangeBetweenEpochs = new BigNumber ( nextEpoch . zrxStaked || 0 )
113- . minus ( new BigNumber ( currentEpoch . zrxStaked || 0 ) )
114- . toNumber ( ) ;
118+ // Reminder: stake change can be negative
119+ zrxStakeChangeBetweenEpochs = new BigNumber ( nextEpoch . zrxStaked || 0 )
120+ . minus ( new BigNumber ( currentEpoch . zrxStaked || 0 ) )
121+ . toNumber ( ) ;
115122
116- // Only allow epochs that have finished into historical data
117- const historicalEpochs = epochRewards
118- ? epochRewards
119- . filter ( ( x ) => ! ! x . epochEndTimestamp )
120- . sort ( ( a , b ) => {
121- return a . epochId - b . epochId ;
122- } )
123- : null ;
123+ // Only allow epochs that have finished into historical data
124+ historicalEpochs = epochRewards
125+ ? epochRewards
126+ . filter ( ( x ) => ! ! x . epochEndTimestamp )
127+ . sort ( ( a , b ) => {
128+ return a . epochId - b . epochId ;
129+ } )
130+ : null ;
124131
125- const fullyStakedZrx = nextEpoch . zrxStaked / ( nextEpoch . approximateStakeRatio || 1 ) ;
126- const zrxToStaked = Math . max ( fullyStakedZrx - nextEpoch . zrxStaked , 0 ) ;
132+ const fullyStakedZrx = nextEpoch . zrxStaked / ( nextEpoch . approximateStakeRatio || 1 ) ;
133+ zrxToStaked = Math . max ( fullyStakedZrx - nextEpoch . zrxStaked , 0 ) ;
134+ }
127135
128136 return (
129137 < StakingPageLayout isHome = { true } title = "Staking pool" >
130- < DashboardHero
131- title = { stakingUtils . getPoolDisplayName ( stakingPool ) }
132- websiteUrl = { stakingPool . metaData . websiteUrl }
133- poolId = { stakingPool . poolId }
134- operatorAddress = { stakingPool . operatorAddress }
135- isVerified = { stakingPool . metaData . isVerified }
136- estimatedStake = { nextEpoch . approximateStakeRatio * 100 }
137- zrxToStaked = { zrxToStaked }
138- rewardsShared = { ( 1 - nextEpoch . operatorShare ) * 100 }
139- iconUrl = { stakingPool . metaData . logoUrl }
140- networkId = { networkId }
141- tabs = { [
142- {
143- title : 'Current Epoch' ,
144- metrics : [
145- // todo(johnrjj) Cutting volume for MVP
146- // {
147- // title: 'Total Volume',
148- // number: '1.23M USD',
149- // },
150- {
151- title : 'APY (last 7 epochs)' ,
152- number : `${ formatPercent ( stakingPoolAPY * 100 || 0 ) . minimized } %` ,
153- } ,
154- {
155- title : 'Fees Generated' ,
156- // 4 decimals looks better here to keep it from wrapping
157- number : `${
158- formatEther ( currentEpoch . totalProtocolFeesGeneratedInEth , {
159- decimals : 4 ,
160- decimalsRounded : 4 ,
161- } ) . minimized
162- } ETH`,
163- } ,
164- {
165- title : 'ZRX Staked' ,
166- number : `${ formatZrx ( nextEpoch . zrxStaked , { bigUnitPostfix : true } ) . formatted } ` ,
167- headerComponent : ( ) => (
168- < InfoTooltip id = "next-epoch-staked-balance" >
169- < div >
170- < div >
171- < TooltipLabel > Current Epoch:</ TooltipLabel > { ' ' }
172- {
173- formatZrx ( currentEpoch . zrxStaked , {
174- decimals : 2 ,
175- decimalsRounded : 2 ,
176- roundDown : true ,
177- } ) . full
178- }
179- </ div >
138+ { stakingPool && (
139+ < DashboardHero
140+ title = { stakingUtils . getPoolDisplayName ( stakingPool ) }
141+ websiteUrl = { stakingPool . metaData . websiteUrl }
142+ poolId = { stakingPool . poolId }
143+ operatorAddress = { stakingPool . operatorAddress }
144+ isVerified = { stakingPool . metaData . isVerified }
145+ estimatedStake = { nextEpoch . approximateStakeRatio * 100 }
146+ zrxToStaked = { zrxToStaked }
147+ rewardsShared = { ( 1 - nextEpoch . operatorShare ) * 100 }
148+ iconUrl = { stakingPool . metaData . logoUrl }
149+ networkId = { networkId }
150+ tabs = { [
151+ {
152+ title : 'Current Epoch' ,
153+ metrics : [
154+ // todo(johnrjj) Cutting volume for MVP
155+ // {
156+ // title: 'Total Volume',
157+ // number: '1.23M USD',
158+ // },
159+ {
160+ title : 'APY (last 3 epochs)' ,
161+ number : `${ formatPercent ( stakingPoolAPY3Epochs * 100 || 0 ) . minimized } %` ,
162+ } ,
163+ {
164+ title : 'Fees Generated' ,
165+ // 4 decimals looks better here to keep it from wrapping
166+ number : `${
167+ formatEther ( currentEpoch . totalProtocolFeesGeneratedInEth , {
168+ decimals : 2 ,
169+ decimalsRounded : 2 ,
170+ } ) . minimized
171+ } ETH`,
172+ } ,
173+ {
174+ title : 'ZRX Staked' ,
175+ number : `${ formatZrx ( nextEpoch . zrxStaked , { bigUnitPostfix : true } ) . formatted } ` ,
176+ headerComponent : ( ) => (
177+ < InfoTooltip id = "next-epoch-staked-balance" >
180178 < div >
181- < TooltipLabel > Pending Epoch:</ TooltipLabel > { ' ' }
182- {
183- formatZrx ( zrxStakeChangeBetweenEpochs , {
184- decimals : 2 ,
185- decimalsRounded : 2 ,
186- roundDown : true ,
187- positiveSign : zrxStakeChangeBetweenEpochs >= 0 ,
188- } ) . full
189- }
179+ < div >
180+ < TooltipLabel > Current Epoch:</ TooltipLabel > { ' ' }
181+ {
182+ formatZrx ( currentEpoch . zrxStaked , {
183+ decimals : 2 ,
184+ decimalsRounded : 2 ,
185+ roundDown : true ,
186+ } ) . full
187+ }
188+ </ div >
189+ < div >
190+ < TooltipLabel > Pending Epoch:</ TooltipLabel > { ' ' }
191+ {
192+ formatZrx ( zrxStakeChangeBetweenEpochs , {
193+ decimals : 2 ,
194+ decimalsRounded : 2 ,
195+ roundDown : true ,
196+ positiveSign : zrxStakeChangeBetweenEpochs >= 0 ,
197+ } ) . full
198+ }
199+ </ div >
190200 </ div >
191- </ div >
192- </ InfoTooltip >
193- ) ,
194- } ,
201+ </ InfoTooltip >
202+ ) ,
203+ } ,
195204
196- {
197- title : 'Rewards Shared' ,
198- // No good way to show rewards shared of an epoch in progress (currentEpoch) right now.
199- // Defaulting to dash ('-') for now
200- number : `${
201- formatEther ( '0' , {
202- zeroStyled : true ,
203- } ) . formatted
204- } ETH`,
205- } ,
206- ] ,
207- } ,
208- {
209- title : 'All Time' ,
210- metrics : [
211- {
212- title : 'ZRX Staked' ,
213- number : `${ formatZrx ( currentEpoch . zrxStaked ) . minimized } ` ,
214- } ,
215- {
216- title : 'Fees Generated' ,
217- number : `${
218- formatEther ( stakingPool . allTimeStats . protocolFeesGeneratedInEth , {
219- decimalsRounded : 4 ,
220- decimals : 4 ,
221- } ) . formatted
222- } ETH`,
223- } ,
224- {
225- title : 'Rewards Shared' ,
226- number : `${
227- formatEther ( stakingPool . allTimeStats . membersRewardsPaidInEth , {
228- decimals : 4 ,
229- decimalsRounded : 4 ,
230- } ) . formatted
231- } ETH`,
232- } ,
233- {
234- title : 'Number of trades' ,
235- number : stakingPool . allTimeStats . numberOfFills ,
236- } ,
237- ] ,
238- } ,
239- ] }
240- />
205+ {
206+ title : 'Rewards Shared' ,
207+ // No good way to show rewards shared of an epoch in progress (currentEpoch) right now.
208+ // Defaulting to dash ('-') for now
209+ number : `${
210+ formatEther ( '0' , {
211+ zeroStyled : true ,
212+ } ) . formatted
213+ } ETH`,
214+ } ,
215+ ] ,
216+ } ,
217+ {
218+ title : 'All Time' ,
219+ metrics : [
220+ {
221+ title : 'APY (last 12 epochs)' ,
222+ number : `${ formatPercent ( stakingPoolAPY12Epochs * 100 || 0 ) . minimized } %` ,
223+ } ,
224+ {
225+ title : 'Fees Generated' ,
226+ number : `${
227+ formatEther ( stakingPool . allTimeStats . protocolFeesGeneratedInEth , {
228+ decimalsRounded : 2 ,
229+ decimals : 2 ,
230+ } ) . formatted
231+ } ETH`,
232+ } ,
233+ {
234+ title : 'ZRX Staked' ,
235+ number : `${ formatZrx ( nextEpoch . zrxStaked , { bigUnitPostfix : true } ) . formatted } ` ,
236+ } ,
237+ {
238+ title : 'Rewards Shared' ,
239+ number : `${
240+ formatEther ( stakingPool . allTimeStats . membersRewardsPaidInEth , {
241+ decimals : 2 ,
242+ decimalsRounded : 2 ,
243+ } ) . formatted
244+ } ETH`,
245+ } ,
246+ ] ,
247+ } ,
248+ ] }
249+ />
250+ ) }
241251 { /* TODO(johnrjj) Copy from account page when finished */ }
242252 { /* <ActionsWrapper>
243253 <ActionsInner>
0 commit comments