1
+ "use client" ;
2
+
3
+ import { Card } from "@pythnetwork/component-library/Card" ;
4
+ import { Label } from "@pythnetwork/component-library/unstyled/Label" ;
5
+ import { Input } from "@pythnetwork/component-library/unstyled/TextField" ;
6
+ import clsx from "clsx" ;
7
+ import React , { useState , useEffect } from "react" ;
8
+
9
+ import styles from "./index.module.scss" ;
10
+
11
+ // Simple Math component for inline mathematical expressions
12
+ const MathExpression : React . FC < { children : React . ReactNode } > = ( { children } ) => {
13
+ return < span className = "katex" > { children } </ span > ;
14
+ } ;
15
+
16
+ // Component for subscripts and superscripts
17
+ const Sub : React . FC < { children : React . ReactNode } > = ( { children } ) => (
18
+ < sub > { children } </ sub >
19
+ ) ;
20
+ const Sup : React . FC < { children : React . ReactNode } > = ( { children } ) => (
21
+ < sup > { children } </ sup >
22
+ ) ;
23
+
24
+ const RewardSimulator : React . FC = ( ) => {
25
+ const [ publisherStake , setPublisherStake ] = useState ( 200 ) ;
26
+ const [ delegatorStake , setDelegatorStake ] = useState ( 300 ) ;
27
+ const [ maxCap , setMaxCap ] = useState ( 500 ) ;
28
+ const [ delegatorFee , setDelegatorFee ] = useState ( 20 ) ;
29
+ const [ rewardRate , setRewardRate ] = useState ( 10 ) ;
30
+
31
+ const [ publisherReward , setPublisherReward ] = useState ( 0 ) ;
32
+ const [ delegatorReward , setDelegatorReward ] = useState ( 0 ) ;
33
+ const [ publisherRewardRate , setPublisherRewardRate ] = useState ( 0 ) ;
34
+ const [ delegatorRewardRate , setDelegatorRewardRate ] = useState ( 0 ) ;
35
+
36
+ useEffect ( ( ) => {
37
+ const calculateRewards = ( ) => {
38
+ const totalStake = publisherStake + delegatorStake ;
39
+ const eligibleAmount = Math . min ( totalStake , maxCap ) ;
40
+ const totalReward = ( rewardRate / 100 ) * eligibleAmount ;
41
+
42
+ const publisherRewardBase =
43
+ ( rewardRate / 100 ) * Math . min ( publisherStake , maxCap ) ;
44
+ const delegatorRewardBase = totalReward - publisherRewardBase ;
45
+
46
+ const delegatorFeeAmount = ( delegatorFee / 100 ) * delegatorRewardBase ;
47
+
48
+ const finalDelegatorReward = delegatorRewardBase - delegatorFeeAmount ;
49
+ const finalPublisherReward = publisherRewardBase + delegatorFeeAmount ;
50
+
51
+ setPublisherReward ( Number ( finalPublisherReward . toFixed ( 2 ) ) ) ;
52
+ setDelegatorReward ( Number ( finalDelegatorReward . toFixed ( 2 ) ) ) ;
53
+ setPublisherRewardRate (
54
+ Number ( ( ( finalPublisherReward * 100 ) / publisherStake ) . toFixed ( 2 ) ) ,
55
+ ) ;
56
+ setDelegatorRewardRate (
57
+ Number ( ( ( finalDelegatorReward * 100 ) / delegatorStake ) . toFixed ( 2 ) ) ,
58
+ ) ;
59
+ } ;
60
+
61
+ calculateRewards ( ) ;
62
+ } , [ publisherStake , delegatorStake , maxCap , delegatorFee , rewardRate ] ) ;
63
+
64
+ return (
65
+ < Card
66
+ variant = "secondary"
67
+ title = "Reward Simulator"
68
+ nonInteractive
69
+ className = { clsx ( styles . card ) }
70
+ >
71
+ < div className = { clsx ( styles . inputGrid ) } >
72
+ < div className = { clsx ( styles . inputGroup ) } >
73
+ < Label htmlFor = "publisher-stake" >
74
+ Publisher Stake (
75
+ < MathExpression >
76
+ S< Sub > p</ Sub > < Sup > p</ Sup >
77
+ </ MathExpression >
78
+ ):
79
+ </ Label >
80
+ < Input
81
+ id = "publisher-stake"
82
+ type = "number"
83
+ value = { publisherStake }
84
+ onChange = { ( e ) => {
85
+ setPublisherStake ( Number ( e . target . value ) ) ;
86
+ } }
87
+ className = { clsx ( styles . input ) }
88
+ min = "0"
89
+ />
90
+ </ div >
91
+ < div className = { clsx ( styles . inputGroup ) } >
92
+ < Label htmlFor = "delegator-stake" >
93
+ Delegator Stake (
94
+ < MathExpression >
95
+ S< Sub > p</ Sub > < Sup > d</ Sup >
96
+ </ MathExpression >
97
+ ):
98
+ </ Label >
99
+ < Input
100
+ id = "delegator-stake"
101
+ type = "number"
102
+ value = { delegatorStake }
103
+ onChange = { ( e ) => {
104
+ setDelegatorStake ( Number ( e . target . value ) ) ;
105
+ } }
106
+ className = { clsx ( styles . input ) }
107
+ min = "0"
108
+ />
109
+ </ div >
110
+ < div className = { clsx ( styles . inputGroup ) } >
111
+ < Label htmlFor = "max-cap" >
112
+ Maximum Cap (
113
+ < MathExpression >
114
+ C< Sub > p</ Sub >
115
+ </ MathExpression >
116
+ ):
117
+ </ Label >
118
+ < Input
119
+ id = "max-cap"
120
+ type = "number"
121
+ value = { maxCap }
122
+ onChange = { ( e ) => {
123
+ setMaxCap ( Number ( e . target . value ) ) ;
124
+ } }
125
+ className = { clsx ( styles . input ) }
126
+ min = "0"
127
+ />
128
+ </ div >
129
+ < div className = { clsx ( styles . inputGroup ) } >
130
+ < Label htmlFor = "delegator-fee" >
131
+ Delegator Fee (< MathExpression > f</ MathExpression > ) (%):
132
+ </ Label >
133
+ < Input
134
+ id = "delegator-fee"
135
+ type = "number"
136
+ value = { delegatorFee }
137
+ onChange = { ( e ) => {
138
+ setDelegatorFee ( Number ( e . target . value ) ) ;
139
+ } }
140
+ className = { clsx ( styles . input ) }
141
+ min = "0"
142
+ max = "100"
143
+ step = "0.1"
144
+ />
145
+ </ div >
146
+ < div className = { clsx ( styles . inputGroup ) } >
147
+ < Label htmlFor = "reward-rate" >
148
+ Reward Rate (< MathExpression > r</ MathExpression > ) (%):
149
+ </ Label >
150
+ < Input
151
+ id = "reward-rate"
152
+ type = "number"
153
+ value = { rewardRate }
154
+ onChange = { ( e ) => {
155
+ setRewardRate ( Number ( e . target . value ) ) ;
156
+ } }
157
+ className = { clsx ( styles . input ) }
158
+ min = "0"
159
+ max = "100"
160
+ step = "0.1"
161
+ />
162
+ </ div >
163
+ </ div >
164
+ < div className = { clsx ( styles . resultsSection ) } >
165
+ < div className = { clsx ( styles . resultsGrid ) } >
166
+ < div className = { clsx ( styles . resultGroup ) } >
167
+ < h4 className = { clsx ( styles . resultTitle ) } > Calculated Rewards</ h4 >
168
+ < div className = { clsx ( styles . resultValues ) } >
169
+ < p className = { clsx ( styles . resultItem ) } >
170
+ < span className = { clsx ( styles . resultLabel ) } >
171
+ Publisher Reward (
172
+ < MathExpression >
173
+ R< Sup > p</ Sup > < Sub > p</ Sub >
174
+ </ MathExpression >
175
+ ):
176
+ </ span > { " " }
177
+ < span className = { clsx ( styles . resultValue ) } >
178
+ { publisherReward }
179
+ </ span >
180
+ </ p >
181
+ < p className = { clsx ( styles . resultItem ) } >
182
+ < span className = { clsx ( styles . resultLabel ) } >
183
+ Delegator Reward (
184
+ < MathExpression >
185
+ R< Sup > d</ Sup > < Sub > p</ Sub >
186
+ </ MathExpression >
187
+ ):
188
+ </ span > { " " }
189
+ < span className = { clsx ( styles . resultValue ) } >
190
+ { delegatorReward }
191
+ </ span >
192
+ </ p >
193
+ </ div >
194
+ </ div >
195
+ < div className = { clsx ( styles . resultGroup ) } >
196
+ < h4 className = { clsx ( styles . resultTitle ) } >
197
+ Calculated Reward Rates (Yearly)
198
+ </ h4 >
199
+ < div className = { clsx ( styles . resultValues ) } >
200
+ < p className = { clsx ( styles . resultItem ) } >
201
+ < span className = { clsx ( styles . resultLabel ) } >
202
+ Publisher Rate (
203
+ < MathExpression >
204
+ r< Sup > p</ Sup > < Sub > p</ Sub >
205
+ </ MathExpression >
206
+ ):
207
+ </ span > { " " }
208
+ < span className = { clsx ( styles . resultValue ) } >
209
+ { publisherRewardRate } %
210
+ </ span >
211
+ </ p >
212
+ < p className = { clsx ( styles . resultItem ) } >
213
+ < span className = { clsx ( styles . resultLabel ) } >
214
+ Delegator Rate (
215
+ < MathExpression >
216
+ r< Sup > d</ Sup > < Sub > p</ Sub >
217
+ </ MathExpression >
218
+ ):
219
+ </ span > { " " }
220
+ < span className = { clsx ( styles . resultValue ) } >
221
+ { delegatorRewardRate } %
222
+ </ span >
223
+ </ p >
224
+ </ div >
225
+ </ div >
226
+ </ div >
227
+ </ div >
228
+ </ Card >
229
+ ) ;
230
+ } ;
231
+
232
+ export default RewardSimulator ;
0 commit comments