Skip to content

Commit

Permalink
refactor: optimize nervos dao calculator
Browse files Browse the repository at this point in the history
  • Loading branch information
Keith-CY committed Apr 2, 2024
1 parent 26258e7 commit 2820b83
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 57 deletions.
16 changes: 10 additions & 6 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -572,22 +572,26 @@
"deposit_address_tooltip": "Number of addresses with non-zero balance of Nervos DAO",
"deposit_to_dao": "Deposit to Nervos DAO",
"deposit_to_dao_description": "Deposit to receive an equivalent amount of CKB at the same rate as the secondary issuance, keep your assets away from being diluted by the secondary issuance.",
"reward_calculator": "Reward Calculator",
"reward_calculator": "Compensation Calculator",
"nervos_dao_rfc": "Nervos DAO RFC",
"learn_more": "Learn More",
"dao_reward_calculator": "Nervos DAO Reward Calculator",
"deposit_terms": "Nervos DAO requires 102 CKBytes for storing lock records, and this portion of CKBytes does not generate lock rewards. Please refer to the <0>Nervos DAO RFC</0> for more information on Nervos DAO.",
"dao_reward_calculator": "Nervos DAO Compensation Calculator",
"deposit_terms": "Nervos DAO requires 102 CKBytes for storing cell itself, and this portion of CKBytes won't be compensated. Please refer to the <0>Nervos DAO RFC</0> for more information on Nervos DAO.",
"you_deposit": "You Deposit",
"you_can_withdraw": "You can withdraw",
"estimated_rewards": "Make a withdraw request after almost {{days}} days:",
"deposit_notice": "30 days work as a circle, if you didn’t make withdraw request, the interest will be calculated as compound interest.",
"estimated_APC": "Estimated APC",
"exclude_inactive_ckb": "Exclude inactive CKB",
"exclude_inactive_ckb_tip": "",
"estimated_rewards_30": "Estimated Rewards in 30 Days",
"rewards": "Rewards",
"rewards": "Compensation",
"estimated_rewards_in_years": "Estimated compensation in",
"withdrawal": "Withdrawal",
"year": "Year",
"years": "Years",
"day": "Days",
"done": "Done"
"done": "Done",
"view_apc_trending": "View APC Trending"
},
"error": {
"maintain": "The tip block number {{tip}}, the current synced block lagging behind by {{lag}} blocks",
Expand Down
20 changes: 12 additions & 8 deletions src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -571,24 +571,28 @@
"24hrs_update": "24 小时变动(UTC+8:00)",
"today_update": "今日变动(UTC+8:00)",
"deposit_address_tooltip": "Nervos DAO 锁定余额不为零的地址",
"deposit_to_dao": "锁定到Nervos DAO",
"deposit_to_dao": "锁定到 Nervos DAO",
"deposit_to_dao_description": "锁定以获得与二次发行利率相同的等额 CKB,避免您的资产被二次发行稀释。",
"reward_calculator": "奖励计算器",
"reward_calculator": "补贴计算器",
"nervos_dao_rfc": "Nervos DAO RFC",
"learn_more": "了解更多",
"dao_reward_calculator": "Nervos DAO 奖励计算器",
"dao_reward_calculator": "Nervos DAO 补贴计算器",
"deposit_terms": "Nervos DAO 需要 102 CKBytes 作为锁定记录的存储,这部分 CKBytes 是无法产生锁定补贴的。<br />请查看 <0>Nervos DAO RFC</0> 以了解 Nervos DAO 更多信息。",
"you_deposit": "锁定",
"you_can_withdraw": "可领取",
"estimated_rewards": "{{days}}天后提出提款申请:",
"deposit_notice": "30天为一个周期,如果您没有提出提款要求,利息将按复利计算",
"estimated_rewards": "{{days}} 天后提出提取申请:",
"deposit_notice": "30天为一个周期,如果您没有提出提取要求,利息将按复利计算",
"estimated_APC": "预计年化锁定补贴率",
"exclude_inactive_ckb": "不包含非活动的CKB",
"exclude_inactive_ckb_tip": "",
"estimated_rewards_30": "30天预计奖励",
"rewards": "奖励",
"rewards": "补贴",
"estimated_rewards_in_years": "预计补贴",
"withdrawal": "提取",
"year": "",
"years": "",
"day": "",
"done": "完成"
"done": "完成",
"view_apc_trending": "查看 APC 走势"
},
"error": {
"maintain": "最新区块为 {{tip}}, 当前同步高度落后 {{lag}} 区块",
Expand Down
1 change: 1 addition & 0 deletions src/pages/NervosDao/DaoBanner/DaoBanner.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
cursor: pointer;
font-size: 14px;
line-height: 14px;
transition: none;

.icon {
path {
Expand Down
14 changes: 7 additions & 7 deletions src/pages/NervosDao/DaoBanner/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ const DaoBanner = ({ estimatedApc }: { estimatedApc: string }) => {
<div className={styles.actions}>
<button type="button" className={styles.btn} onClick={() => setShowRewardCalcutorModal(true)}>
{t('nervos_dao.reward_calculator')}
<GoIcon className={styles.icon} />
</button>
<button type="button" className={styles.btn} onClick={() => window.open(NERVOS_DAO_RFC_URL)}>
<a className={styles.btn} href={NERVOS_DAO_RFC_URL} target="_blank" rel="noopener noreferrer">
{t('nervos_dao.nervos_dao_rfc')}
<GoIcon className={styles.icon} />
</button>
<button
type="button"
</a>
<a
className={styles.btn}
onClick={() => window.open('https://medium.com/nervosnetwork/nervos-dao-explained-95e33898b1c')}
href="https://www.nervos.org/knowledge-base/nervosdao_withdrawal_process_explained"
target="_blank"
rel="noopener noreferrer"
>
{t('nervos_dao.learn_more')}
<GoIcon className={styles.icon} />
</button>
</a>
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@
.apcTitle {
flex: 1;
margin: 0;

a {
margin-left: 4px;
}
}

div {
Expand Down Expand Up @@ -145,6 +149,16 @@
}
}

.years {
input {
outline: none;
border: 1px solid #e5e5e5;
width: 6ch;
padding: 0 4px;
margin: 0 4px;
}
}

/* stylelint-disable-next-line selector-class-pattern */
:global(.ant-input[disabled]) {
color: #333 !important;
Expand Down
129 changes: 93 additions & 36 deletions src/pages/NervosDao/RewardCalcutorModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,59 @@
import { useState, useMemo } from 'react'
import { Link } from 'react-router-dom'
import { Input } from 'antd'
import { Trans, useTranslation } from 'react-i18next'
import BigNumber from 'bignumber.js'
import CommonModal from '../../../components/CommonModal'
import styles from './RewardCalcutorModal.module.scss'
import { ReactChartCore } from '../../StatisticsChart/common'
import CloseIcon from '../../../assets/modal_close.png'
import { MIN_DEPOSIT_AMOUNT, MAX_DECIMAL_DIGITS, NERVOS_DAO_RFC_URL } from '../../../constants/common'
import { ckbToShannon, shannonToCkb } from '../../../utils/util'
import { MIN_DEPOSIT_AMOUNT, NERVOS_DAO_RFC_URL, IS_MAINNET, MAX_DECIMAL_DIGITS } from '../../../constants/common'
import { localeNumberString } from '../../../utils/number'
import { isMainnet } from '../../../utils/chain'

const INIT_DEPOSIT_VALUE = '1000'

const RewardCalcutorModal = ({ onClose, estimatedApc }: { onClose: () => void; estimatedApc: string }) => {
const { t } = useTranslation()
const [depositValue, setDepositValue] = useState('1000')
const [inputVal, setInputVal] = useState(localeNumberString(depositValue))

const [annualRewards, monthRewards] = useMemo(() => {
const amount = Number(depositValue) - MIN_DEPOSIT_AMOUNT
const value = ckbToShannon((amount > 0 ? amount : 0).toFixed(MAX_DECIMAL_DIGITS).toString())
const [depositValue, setDepositValue] = useState<string>(INIT_DEPOSIT_VALUE)
const [years, setYears] = useState<number>(5)

const dpc = Number(estimatedApc || 0) / 365 / 100
const yearReward = useMemo(() => {
const EMPTY = BigNumber(0)
if (!estimatedApc) return EMPTY
if (!depositValue) return EMPTY
const v = BigNumber(depositValue)

const mRewards = (Number(value) * dpc * 30).toFixed(0).toString()
if (v.isNaN() || v.isNegative()) return EMPTY

const rewerds = (Number(value) * dpc * 360).toFixed(0).toString()
const amount = v.minus(MIN_DEPOSIT_AMOUNT)
if (amount.isNegative()) return EMPTY

return [shannonToCkb(rewerds), shannonToCkb(mRewards)]
const yearReward = amount.multipliedBy(estimatedApc).dividedBy(100)
return yearReward
}, [depositValue, estimatedApc])

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
let { value } = e.target
const monthReward = yearReward.dividedBy(12)

value = value.replace('。', '.').replace(/^\D*([0-9]\d*\.?\d{0,8})?.*$/, '$1')
const handleDepositChange = (e: React.ChangeEvent<HTMLInputElement>) => {
e.stopPropagation()
e.preventDefault()
const { value } = e.currentTarget
const v = value.replace(/,/g, '')
if (!v) {
setDepositValue('')
return
}
setDepositValue(v)
}

setInputVal(value)
setDepositValue(value)
const handleYearChange = (e: React.ChangeEvent<HTMLInputElement>) => {
e.stopPropagation()
e.preventDefault()
const y = +e.currentTarget.value
if (y < 1) {
return
}
setYears(y)
}

return (
Expand Down Expand Up @@ -62,56 +81,94 @@ const RewardCalcutorModal = ({ onClose, estimatedApc }: { onClose: () => void; e
<div className={styles.modalContent}>
<h2>{t('nervos_dao.you_deposit')}</h2>
<Input
value={inputVal}
onFocus={() => setInputVal(depositValue)}
onBlur={() => setInputVal(localeNumberString(depositValue))}
maxLength={15}
value={
/\.$/.test(depositValue)
? `${localeNumberString(depositValue.slice(0, -1))}.`
: localeNumberString(depositValue)
}
className={styles.input}
suffix="CKB"
onChange={handleInputChange}
onChange={handleDepositChange}
/>
<h2>{t('nervos_dao.you_can_withdraw')}</h2>
<p>{t(`nervos_dao.estimated_rewards`, { days: 30 })}</p>
<Input value={localeNumberString(monthRewards)} className={styles.input} suffix="CKB" disabled />
<Input
value={localeNumberString(monthReward.plus(depositValue).toFixed(MAX_DECIMAL_DIGITS))}
className={styles.input}
suffix="CKB"
disabled
/>
<p>{t(`nervos_dao.estimated_rewards`, { days: 360 })}</p>
<Input value={localeNumberString(annualRewards)} className={styles.input} suffix="CKB" disabled />
<Input
value={localeNumberString(yearReward.plus(depositValue).toFixed(MAX_DECIMAL_DIGITS))}
className={styles.input}
suffix="CKB"
disabled
/>
<div className={styles.notice}>{t('nervos_dao.deposit_notice')}</div>

<div className={styles.apcHeader}>
<h2 className={styles.apcTitle}>{t('nervos_dao.estimated_APC')}</h2>
<h2 className={styles.apcTitle}>
{t('nervos_dao.estimated_APC')}
<Link to="charts/nominal-apc" target="_blank">
{`(${t('nervos_dao.view_apc_trending')})`}
</Link>
</h2>
</div>
<Input value={`${estimatedApc}%`} className={styles.input} disabled />

<h2>{t('nervos_dao.estimated_rewards_30')}</h2>
<h2 className={styles.years}>
{t('nervos_dao.estimated_rewards_in_years')}
<input onChange={handleYearChange} value={years} type="number" min="1" />
{t('nervos_dao.years')}
</h2>
<div className={styles.chartWap}>
<p className={styles.yTitle}>{t('nervos_dao.rewards')}</p>
<p className={styles.yTitle}>{t('nervos_dao.withdrawal')}</p>
<ReactChartCore
option={{
color: [isMainnet() ? '#00cc9b' : '#9a2cec'],
color: IS_MAINNET ? ['#00cc9b'] : ['#9a2cec'],
tooltip: {
show: true,
formatter: '{c} CKB',
},
grid: { top: 12, right: 30, bottom: 80, left: 78 },
xAxis: {
type: 'category',
boundaryGap: false,
data: ['0', '30'],
data: Array.from({ length: years }, (_, i) => i + 1),
axisLabel: {
formatter: (value: string) =>
+value > 1 ? `${value} ${t('nervos_dao.years')}` : `${value} ${t('nervos_dao.year')}`,
},
},
yAxis: {
type: 'value',
axisLabel: {
formatter: (value: string) => `${value} CKB`,
},
boundaryGap: ['5%', '2%'],
},
series: [
{
data: [0, Number(monthRewards)],
data: Array.from({ length: years }, (_, i) => yearReward.multipliedBy(i + 1).toNumber()),
type: 'line',
stack: 'withdrawal',
areaStyle: {},
label: {
normal: {
show: years <= 5,
position: 'top',
formatter: '{c} CKB',
},
},
},
],
}}
notMerge
lazyUpdate
style={{
height: '100%',
width: '100%',
}}
style={{ height: '100%', width: '100%' }}
/>
<p className={styles.xTitle}>{t('nervos_dao.day')}</p>
<p className={styles.xTitle}>{t('nervos_dao.years')}</p>
</div>
</div>

Expand Down
10 changes: 10 additions & 0 deletions src/styles/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ a {
white-space: pre-wrap;
}

.ant-input:focus {
border-color: none;
}

.ant-input-affix-wrapper:hover,
.ant-input-affix-wrapper:focus {
border-color: #e5e5e5 !important;
box-shadow: none;
}

a:hover,
.ant-btn-primary:hover,
.ant-tabs-tab:hover {
Expand Down

0 comments on commit 2820b83

Please sign in to comment.