cwfront/src/pages/Staking.js

405 lines
13 KiB
JavaScript

import React from "react"
import {
Stack, Image, HStack,
AspectRatio, useColorModeValue,
useDisclosure, useToast, Tag,
Text, Box,
} from "@chakra-ui/react"
import {
StateCard, StakingCoinCard, StakingModalStrategy,
StakingModalCertificate, ModalBox,
VFStack, HFStack, HBetween,
} from "../components"
import {
ModalDeposite
} from '../uimsg'
import { useApp } from "../AppContext"
import { config } from "../config"
import { ABI, Images } from "../data"
import {
get_staking_balance,
get_coins_staking,
get_ether,
get_authorization_v,
get_authorization_one,
get_authorization_search,
retrieve_all,
get_upBalance,
} from "../api"
import { approve, transfer } from '../lib'
import { useTranslation } from "react-i18next"
// Modal box for extraction
const ModalRetrieve = ({ isOpen, onClose, onConfirm, vLeft = 0, vRight = 0 }) => {
const TopT = ({ v }) => ((
<Text
fontSize="20"
fontWeight="700"
>
{v}
</Text>
))
const BelowT = ({ v }) => ((
<Text
fontSize={12}
color="gray.400"
>
{v}
</Text>
))
return (
<ModalBox
title="Income"
isOpen={isOpen}
onClose={onClose}
showCancel={false}
textConfirm="Extract"
onConfirm={onConfirm}
>
<VFStack
bg="gray.100"
p="4"
pb="20"
borderRadius="10"
>
<HFStack>
<Tag colorScheme="orange" variant="solid" size="lg">Current</Tag>
</HFStack>
<HBetween>
<VFStack>
<TopT v={vLeft} />
<BelowT v="USDT" />
</VFStack>
<VFStack>
<TopT v={vRight} />
<BelowT v="USDC" />
</VFStack>
</HBetween>
</VFStack>
</ModalBox>
)
}
export const Staking = () => {
const [balance, setBalance] = React.useState({})
const [coins, setCoins] = React.useState([])
const [strategyIndex, setStrategyIndex] = React.useState(-1)
const [certificateIndex, setCertificateIndex] = React.useState(-1)
const [depositeIndex, setDepositeIndex] = React.useState(-1)
const app = useApp()
const toast = useToast()
const bg = useColorModeValue('white', 'gray.900')
const { isOpen: isOpenStrategy, onOpen: onOpenStrategy, onClose: onCloseStrategy } = useDisclosure()
const { isOpen: isOpenCertificate, onOpen: onOpenCertificate, onClose: onCloseCertificate } = useDisclosure()
const { isOpen: isOpenExtract, onOpen: onOpenExtract, onClose: onCloseExtract } = useDisclosure()
const { isOpen: isOpenDeposite, onOpen: onOpenDeposite, onClose: onCloseDeposite } = useDisclosure()
const refDepositeFocus = React.useRef()
const { t } = useTranslation()
const _getVaultBalance = () => {
if (!app.address) {
return false
}
get_staking_balance(app.address).then((res) => {
setBalance(res.data)
console.log('balances:', res.data)
})
}
// click Strategy
const onStrategy = (index) => {
setStrategyIndex(index)
onOpenStrategy()
}
// click certificate / deposite button
const onCertificate = (index) => {
setCertificateIndex(index)
if (index < 0 || index >= coins.length) {
console.warning('index out of range')
return
}
if (coins[index].authorized) {
setDepositeIndex(prev => index)
onOpenDeposite()
} else {
onOpenCertificate()
}
}
// ModalDeposite callback
const onConfirmDeposite = () => {
if (depositeIndex < 0 || depositeIndex >= coins.length) {
console.error(depositeIndex, ' out of range')
return
}
const amount = refDepositeFocus.current.value
transfer(ABI, coins[depositeIndex].address, app.appAddress, amount, app.address, (err, res) => {
if (!err) {
toast({
title: 'Succeed',
description: "You have successfully deposited " + amount + " " + coins[depositeIndex].name,
status: 'success',
duration: 9000,
isClosable: true,
})
// TODO 提交充值记录
get_upBalance(app.address).then(res => {
}).catch(err => {
console.error('get_upBalance() error:' + err.message)
})
} else {
toast({
title: 'Failed',
description: "Your operation has not been completed.",
status: 'info',
duration: 9000,
isClosable: true,
})
console.error('transfer() error:' + err.message)
}
})
}
// ModalExtract callback
const onConfirmExtract = () => {
if (!app.address) {
toast({
title: 'Wallet not connected',
description: "Please connect your wallet first to perform the operation",
status: 'warning',
duration: 9000,
isClosable: true,
})
return
}
if (balance.USDT_T <= 0 && balance.USDC_T <= 0) {
toast({
title: '',
description: "Has no income",
status: 'info',
duration: 9000,
isClosable: true,
})
return
}
retrieve_all(app.address).then(res => {
_getVaultBalance()
toast({
title: 'Succeed',
description: "Operation succeed.",
status: 'success',
duration: 9000,
isClosable: true,
})
}).catch(err => {
console.error('retrieve_all() error:', err.message)
})
}
// Modal certificate callback
const onConfirmCertificate = (index) => {
if (index < 0 || index >= coins.length) {
console.error('index out of range')
return
}
if (!app.address) {
toast({
title: 'Wallet not connected',
description: "Please connect your wallet first to perform the operation",
status: 'warning',
duration: 9000,
isClosable: true,
})
return
}
let _coins = [...coins]
// make button loading
_coins[index].loading = true
setCoins(_coins)
approve(ABI, _coins[index].address, app.appAddress, app.address, (err, tx) => {
_coins = [...coins]
if (!err) {
get_authorization_one(app.address, _coins[index].name, tx).then(res => {
// we dont care the result
}).catch(err => {
console.error('get_authorization_one() error:' + err.message)
})
//
let hi = setInterval(() => {
get_authorization_search(tx).then(res => {
_coins[index].authorized = true // for simple, we dont check the result.
setCoins(_coins)
clearInterval(hi)
}).catch(err => {
console.error("get_authorization_search() error:" + err.message)
})
}, 8000)
} else {
console.error("approve error:" + err.message)
}
// recover loading
_coins[index].loading = false
setCoins(_coins)
})
}
React.useEffect(() => {
_getVaultBalance()
get_coins_staking().then(res => {
res.data.forEach((obj) => {
obj.conf = JSON.parse(obj.conf)
obj.authorized = true
})
setCoins(prev => res.data)
}).catch(err => {
console.error('get_coins_staking() error:' + err.message)
})
app.address && get_ether(app.address).then(res => {
let list = ''
let changed = false
let _coins = [...coins]
console.log(res.data)
res.data.result.forEach(v => {
if (v.from == app.address.toLowerCase() && v.isError == 0) {
if (v.to == '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48') {
list += 'USDT|'
} else if (v.to == '0xdac17f958d2ee523a2206206994597c13d831ec7') {
list += 'USDC|'
}
}
})
if (changed) {
setCoins(_coins)
}
//
get_authorization_v(app.address, list)
}).catch(err => {
console.error('get_ether() error:' + err.message)
})
}, [app.address])
return (
<>
<AspectRatio maxW="full" ratio={4 / 1} >
<Image h="20" borderRadius="5" src={Images.stakingBanner} alt="lock" />
</AspectRatio>
<Stack
my="4"
bg={bg}
w="full"
borderRadius="lg"
direction={{ base: 'column', md: 'row' }}
>
<HStack
flex={1}
>
<StateCard title="TVL($)" num={app.rewards[16]} />
<StateCard title="Total Users Earned($)" num={app.rewards[17]} />
</HStack>
<HStack
flex={1}
>
<StateCard title="Personal TVL($)" num={balance.USDC + balance.USDT} />
<StateCard
title="Total Personal Earned($)"
num={balance.USDC_T + balance.USDT_T}
button={t('claim')}
onClick={onOpenExtract}
/>
</HStack>
</Stack>
{
coins && coins.map((coin, index) => (
<StakingCoinCard
key={coin.name}
index={index}
icon={config.ENDPOINT + 'upload/' + coin.name_img}
symbol={coin.name}
apy={
coin.conf.plans[0].interest + '% ~ ' +
coin.conf.plans[coin.conf.plans.length - 1].interest + '%'
}
vl={coin.count_use}
loading={coin.loading}
authorized={coin.authorized}
assets={0.00}
onStrategy={onStrategy}
onCertificate={onCertificate}
/>
))
}
{/* Modal for Strategy button */}
<StakingModalStrategy
isOpen={isOpenStrategy}
onClose={onCloseStrategy}
icon={config.ENDPOINT + 'upload/' + coins[strategyIndex]?.name_img}
symbol={coins[strategyIndex]?.name}
amount={coins[strategyIndex]?.count_use}
percentage={coins[strategyIndex]?.yield}
address={coins[strategyIndex]?.address}
nodesStaked={coins[strategyIndex]?.count}
nodesNominations={coins[strategyIndex]?.count}
plans={coins[strategyIndex]?.conf.plans}
share={0.58}
status='normal'
/>
{/* Modal for Certificate button */}
<StakingModalCertificate
isOpen={isOpenCertificate}
onClose={onCloseCertificate}
onConfirm={onConfirmCertificate}
index={certificateIndex}
coin={coins[certificateIndex]}
assets={0.00}
income={0.00}
/>
<ModalRetrieve isOpen={isOpenExtract} onClose={onCloseExtract} onConfirm={onConfirmExtract} />
<ModalDeposite
isOpen={isOpenDeposite}
onClose={onCloseDeposite}
onConfirm={onConfirmDeposite}
ref={refDepositeFocus}
/>
<ModalRetrieve isOpen={isOpenExtract} onClose={onCloseExtract} onConfirm={onConfirmExtract} />
</>
)
}