feat: Board
This commit is contained in:
parent
bb561b4453
commit
977b6fd61b
|
|
@ -0,0 +1,17 @@
|
||||||
|
import contracts from './contracts'
|
||||||
|
import tokens from './tokens'
|
||||||
|
import { BoardConfig } from './types'
|
||||||
|
|
||||||
|
const BoardList: BoardConfig[] = [
|
||||||
|
{
|
||||||
|
pid: 1,
|
||||||
|
name: 'Nodes',
|
||||||
|
img: 'nodes',
|
||||||
|
stringId: 100004,
|
||||||
|
tokenSymbol: tokens.hcc.symbol,
|
||||||
|
tokenAddresses: tokens.hcc.address,
|
||||||
|
tokenDecimals: 18,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default BoardList
|
||||||
|
|
@ -177,3 +177,12 @@ export interface LotteryTicketClaimData {
|
||||||
cakeTotal: BigNumber
|
cakeTotal: BigNumber
|
||||||
roundId: string
|
roundId: string
|
||||||
}
|
}
|
||||||
|
export interface BoardConfig {
|
||||||
|
pid: number
|
||||||
|
name: string
|
||||||
|
img: string
|
||||||
|
stringId?: number
|
||||||
|
tokenSymbol: string
|
||||||
|
tokenAddresses: Address
|
||||||
|
tokenDecimals: number
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ export {
|
||||||
} from './pools'
|
} from './pools'
|
||||||
export { setUserInfo, clearUserInfo } from './userInfo'
|
export { setUserInfo, clearUserInfo } from './userInfo'
|
||||||
export { fetchReferralInfoAsync } from './referral'
|
export { fetchReferralInfoAsync } from './referral'
|
||||||
|
export { fetchBoardsPublicDataAsync, fetchBoardUserDataAsync } from './boards'
|
||||||
export { profileFetchStart, profileFetchSucceeded, profileFetchFailed } from './profile'
|
export { profileFetchStart, profileFetchSucceeded, profileFetchFailed } from './profile'
|
||||||
export { fetchStart, teamFetchSucceeded, fetchFailed, teamsFetchSucceeded } from './teams'
|
export { fetchStart, teamFetchSucceeded, fetchFailed, teamsFetchSucceeded } from './teams'
|
||||||
export { setBlock } from './block'
|
export { setBlock } from './block'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import BigNumber from 'bignumber.js'
|
||||||
|
import erc20 from 'config/abi/erc20.json'
|
||||||
|
import boardchefABI from 'config/abi/board.json'
|
||||||
|
import multicall from 'utils/multicall'
|
||||||
|
import { getAddress, getBoardAddress } from 'utils/addressHelpers'
|
||||||
|
import boardsConfig from 'config/constants/boards'
|
||||||
|
|
||||||
|
const fetchBoards = async () => {
|
||||||
|
const data = await Promise.all(
|
||||||
|
boardsConfig.map(async (boardConfig) => {
|
||||||
|
const tokenAddress = getAddress(boardConfig.tokenAddresses)
|
||||||
|
const BoardChefAddress = getBoardAddress()
|
||||||
|
const calls = [
|
||||||
|
// 查节点的代币数量
|
||||||
|
{
|
||||||
|
address: tokenAddress,
|
||||||
|
name: 'balanceOf',
|
||||||
|
params: [BoardChefAddress],
|
||||||
|
},
|
||||||
|
// Token decimals
|
||||||
|
{
|
||||||
|
address: tokenAddress,
|
||||||
|
name: 'decimals',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
|
let [tokenBalance, tokenDecimals] = await multicall(erc20, calls)
|
||||||
|
tokenBalance = new BigNumber(tokenBalance).div(new BigNumber(10).pow(tokenDecimals))
|
||||||
|
let [minStakeAmount] = await multicall(boardchefABI, [
|
||||||
|
// 最低质押额度
|
||||||
|
{
|
||||||
|
address: BoardChefAddress,
|
||||||
|
name: 'firstStakeThreshold',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
minStakeAmount = new BigNumber(minStakeAmount).div(new BigNumber(10).pow(tokenDecimals)).toJSON()
|
||||||
|
return {
|
||||||
|
...boardConfig,
|
||||||
|
tokenBalance: tokenBalance.toJSON(),
|
||||||
|
minStakeAmount,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export default fetchBoards
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
import BigNumber from 'bignumber.js'
|
||||||
|
import erc20ABI from 'config/abi/erc20.json'
|
||||||
|
import boardABI from 'config/abi/board.json'
|
||||||
|
import multicall from 'utils/multicall'
|
||||||
|
import boardsConfig from 'config/constants/boards'
|
||||||
|
import { getAddress, getBoardAddress } from 'utils/addressHelpers'
|
||||||
|
import contracts from 'config/constants/contracts'
|
||||||
|
import tokens from 'config/constants/tokens'
|
||||||
|
|
||||||
|
export const fetchBoardUserAllowances = async (account: string) => {
|
||||||
|
const calls = boardsConfig.map((board) => {
|
||||||
|
const tokenAddresses = getAddress(board.tokenAddresses)
|
||||||
|
const boardChefAdress = getBoardAddress()
|
||||||
|
return { address: tokenAddresses, name: 'allowance', params: [account, boardChefAdress] }
|
||||||
|
})
|
||||||
|
|
||||||
|
const rawLpAllowances = await multicall(erc20ABI, calls)
|
||||||
|
const parsedLpAllowances = rawLpAllowances.map((balance) => {
|
||||||
|
return new BigNumber(balance).toJSON()
|
||||||
|
})
|
||||||
|
return parsedLpAllowances
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchBoardUserTokenBalances = async (account: string) => {
|
||||||
|
const calls = boardsConfig.map((board) => {
|
||||||
|
const tokenAddresses = getAddress(board.tokenAddresses)
|
||||||
|
return {
|
||||||
|
address: tokenAddresses,
|
||||||
|
name: 'balanceOf',
|
||||||
|
params: [account],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const rawTokenBalances = await multicall(erc20ABI, calls)
|
||||||
|
const parsedTokenBalances = rawTokenBalances.map((tokenBalance) => {
|
||||||
|
return new BigNumber(tokenBalance).toJSON()
|
||||||
|
})
|
||||||
|
return parsedTokenBalances
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchBoardUserStakedBalances = async (account: string) => {
|
||||||
|
const calls = boardsConfig.map((board) => {
|
||||||
|
const boardChefAdress = getBoardAddress()
|
||||||
|
return {
|
||||||
|
address: boardChefAdress,
|
||||||
|
name: 'userInfo',
|
||||||
|
params: [account],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const rawStakedBalances = await multicall(boardABI, calls)
|
||||||
|
const parsedStakedBalances = rawStakedBalances.map((stakedBalance) => {
|
||||||
|
return {
|
||||||
|
stakedBalance: new BigNumber(stakedBalance[0]._hex).toJSON(),
|
||||||
|
stakedTime: new BigNumber(stakedBalance[1]._hex).toNumber(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return parsedStakedBalances
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchBoardUserUnlockTime = async (account: string) => {
|
||||||
|
const calls = boardsConfig.map((board) => {
|
||||||
|
const boardChefAdress = getBoardAddress()
|
||||||
|
return {
|
||||||
|
address: boardChefAdress,
|
||||||
|
name: 'boardToPeriod',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const rawUnlockTime = await multicall(boardABI, calls)
|
||||||
|
const parsedUnlockTime = rawUnlockTime.map((time) => {
|
||||||
|
return new BigNumber(time[0]._hex).toNumber()
|
||||||
|
})
|
||||||
|
return parsedUnlockTime
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchUserXCandyBalance = async (account: string) => {
|
||||||
|
const calls = boardsConfig.map((board) => {
|
||||||
|
return {
|
||||||
|
address: getAddress(tokens.hcc.address),
|
||||||
|
name: 'balanceOf',
|
||||||
|
params: [account],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const rawXTokenBalance = await multicall(erc20ABI, calls)
|
||||||
|
const parsedXTokenBalance = rawXTokenBalance.map((item) => {
|
||||||
|
return new BigNumber(item[0]._hex).toJSON()
|
||||||
|
})
|
||||||
|
return parsedXTokenBalance
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
import { createSlice } from '@reduxjs/toolkit'
|
||||||
|
import BigNumber from 'bignumber.js'
|
||||||
|
import boardsConfig from 'config/constants/boards'
|
||||||
|
import fetchBoards from './fetchBoards'
|
||||||
|
import {
|
||||||
|
fetchBoardUserAllowances,
|
||||||
|
fetchBoardUserTokenBalances,
|
||||||
|
fetchBoardUserStakedBalances,
|
||||||
|
fetchBoardUserUnlockTime,
|
||||||
|
fetchUserXCandyBalance,
|
||||||
|
} from './fetchBoardsUser'
|
||||||
|
import { BoardsState, Boards } from '../types'
|
||||||
|
|
||||||
|
const initialState: BoardsState = { data: [...boardsConfig] }
|
||||||
|
|
||||||
|
export const boardsSlice = createSlice({
|
||||||
|
name: 'Boards',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setBoardsPublicData: (state, action) => {
|
||||||
|
const liveBoardsData: Boards[] = action.payload
|
||||||
|
state.data = state.data.map((board) => {
|
||||||
|
const liveBoardData = liveBoardsData.find((f) => f.pid === board.pid)
|
||||||
|
return { ...board, ...liveBoardData }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setBoardsUserData: (state, action) => {
|
||||||
|
const { arrayOfUserDataObjects } = action.payload
|
||||||
|
arrayOfUserDataObjects.forEach((userDataEl) => {
|
||||||
|
const { index } = userDataEl
|
||||||
|
const dataItem = { ...state.data[index] }
|
||||||
|
state.data[index] = { ...dataItem, userData: { ...userDataEl } }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
export const { setBoardsPublicData, setBoardsUserData } = boardsSlice.actions
|
||||||
|
// Thunks
|
||||||
|
export const fetchBoardsPublicDataAsync = () => async (dispatch) => {
|
||||||
|
const nodes = await fetchBoards()
|
||||||
|
dispatch(setBoardsPublicData(nodes))
|
||||||
|
}
|
||||||
|
export const fetchBoardUserDataAsync = (account) => async (dispatch) => {
|
||||||
|
const userBoardAllowances = await fetchBoardUserAllowances(account)
|
||||||
|
const userBoardTokenBalances = await fetchBoardUserTokenBalances(account)
|
||||||
|
const userStakedBalances = await fetchBoardUserStakedBalances(account)
|
||||||
|
const userUnlockTimes = await fetchBoardUserUnlockTime(account)
|
||||||
|
const userXCandyBalances = await fetchUserXCandyBalance(account)
|
||||||
|
const arrayOfUserDataObjects = userBoardAllowances.map((nodeAllowance, index) => {
|
||||||
|
return {
|
||||||
|
index,
|
||||||
|
allowance: userBoardAllowances[index],
|
||||||
|
tokenBalance: userBoardTokenBalances[index],
|
||||||
|
stakedBalance: userStakedBalances[index].stakedBalance,
|
||||||
|
xCandyBalance: userXCandyBalances[index],
|
||||||
|
unlockTime: userUnlockTimes[index] + userStakedBalances[index].stakedTime,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch(setBoardsUserData({ arrayOfUserDataObjects }))
|
||||||
|
}
|
||||||
|
|
||||||
|
export default boardsSlice.reducer
|
||||||
|
|
@ -34,6 +34,7 @@ import {
|
||||||
ReduxNodeLedger,
|
ReduxNodeLedger,
|
||||||
NodeLedger,
|
NodeLedger,
|
||||||
ReduxNodeRound,
|
ReduxNodeRound,
|
||||||
|
Boards,
|
||||||
} from './types'
|
} from './types'
|
||||||
import { fetchProfile } from './profile'
|
import { fetchProfile } from './profile'
|
||||||
import { fetchTeam, fetchTeams } from './teams'
|
import { fetchTeam, fetchTeams } from './teams'
|
||||||
|
|
@ -197,7 +198,29 @@ export const usePoolFromPid = (sousId: number): Pool => {
|
||||||
const pool = useSelector((state: State) => state.pools.data.find((p) => p.sousId === sousId))
|
const pool = useSelector((state: State) => state.pools.data.find((p) => p.sousId === sousId))
|
||||||
return transformPool(pool)
|
return transformPool(pool)
|
||||||
}
|
}
|
||||||
|
// board
|
||||||
|
// boards
|
||||||
|
export const useBoards = (): Boards[] => {
|
||||||
|
const boards = useSelector((state: State) => state.boards.data)
|
||||||
|
return boards
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useBoardsFromPid = (pid): Boards => {
|
||||||
|
const node = useSelector((state: State) => state.boards.data.find((p) => p.pid === pid))
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useNodeUser = (pid) => {
|
||||||
|
const node = useBoardsFromPid(pid)
|
||||||
|
return {
|
||||||
|
allowance: node.userData ? new BigNumber(node.userData.allowance) : new BigNumber(0),
|
||||||
|
tokenBalance: node.userData ? new BigNumber(node.userData.tokenBalance) : new BigNumber(0),
|
||||||
|
stakedBalance: node.userData ? new BigNumber(node.userData.stakedBalance) : new BigNumber(0),
|
||||||
|
unlockTime: node.userData ? node.userData.unlockTime : 0,
|
||||||
|
xCandyBalance: node.userData ? node.userData.xCandyBalance : 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// cake
|
||||||
export const useFetchCakeVault = () => {
|
export const useFetchCakeVault = () => {
|
||||||
const { account } = useWeb3React()
|
const { account } = useWeb3React()
|
||||||
const { fastRefresh } = useRefresh()
|
const { fastRefresh } = useRefresh()
|
||||||
|
|
@ -582,7 +605,7 @@ export const useFetchLottery = () => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentLotteryId) {
|
if (currentLotteryId) {
|
||||||
// Get historical lottery data from nodes + subgraph
|
// Get historical lottery data from boards + subgraph
|
||||||
dispatch(fetchPublicLotteries({ currentLotteryId }))
|
dispatch(fetchPublicLotteries({ currentLotteryId }))
|
||||||
// get public data for current lottery
|
// get public data for current lottery
|
||||||
dispatch(fetchCurrentLottery({ currentLotteryId }))
|
dispatch(fetchCurrentLottery({ currentLotteryId }))
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import votingReducer from './voting'
|
||||||
import lotteryReducer from './lottery'
|
import lotteryReducer from './lottery'
|
||||||
import userInfo from './userInfo'
|
import userInfo from './userInfo'
|
||||||
import referral from './referral'
|
import referral from './referral'
|
||||||
|
import boards from './boards'
|
||||||
|
|
||||||
import application from './application/reducer'
|
import application from './application/reducer'
|
||||||
import { updateVersion } from './global/actions'
|
import { updateVersion } from './global/actions'
|
||||||
|
|
@ -38,6 +39,7 @@ const store = configureStore({
|
||||||
lottery: lotteryReducer,
|
lottery: lotteryReducer,
|
||||||
userInfo,
|
userInfo,
|
||||||
referral,
|
referral,
|
||||||
|
boards,
|
||||||
// Exchange
|
// Exchange
|
||||||
application,
|
application,
|
||||||
user,
|
user,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,16 @@ import { ThunkAction } from 'redux-thunk'
|
||||||
import { AnyAction } from '@reduxjs/toolkit'
|
import { AnyAction } from '@reduxjs/toolkit'
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
import { ethers } from 'ethers'
|
import { ethers } from 'ethers'
|
||||||
import { CampaignType, FarmConfig, LotteryStatus, LotteryTicket, Nft, PoolConfig, Team } from 'config/constants/types'
|
import {
|
||||||
|
CampaignType,
|
||||||
|
FarmConfig,
|
||||||
|
LotteryStatus,
|
||||||
|
LotteryTicket,
|
||||||
|
Nft,
|
||||||
|
PoolConfig,
|
||||||
|
Team,
|
||||||
|
BoardConfig,
|
||||||
|
} from 'config/constants/types'
|
||||||
|
|
||||||
export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, State, unknown, AnyAction>
|
export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, State, unknown, AnyAction>
|
||||||
|
|
||||||
|
|
@ -499,6 +508,20 @@ export interface Referral {
|
||||||
export interface ReferralState {
|
export interface ReferralState {
|
||||||
data: Referral
|
data: Referral
|
||||||
}
|
}
|
||||||
|
export interface Boards extends BoardConfig {
|
||||||
|
tokenAmount?: BigNumber
|
||||||
|
minStakeAmount?: number
|
||||||
|
userData?: {
|
||||||
|
allowance: BigNumber
|
||||||
|
tokenBalance: BigNumber
|
||||||
|
stakedBalance: BigNumber
|
||||||
|
unlockTime: number
|
||||||
|
xCandyBalance: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface BoardsState {
|
||||||
|
data: Boards[]
|
||||||
|
}
|
||||||
// Global state
|
// Global state
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
|
|
@ -513,5 +536,6 @@ export interface State {
|
||||||
voting: VotingState
|
voting: VotingState
|
||||||
userInfo: UserInfoState
|
userInfo: UserInfoState
|
||||||
referral: ReferralState
|
referral: ReferralState
|
||||||
|
boards: BoardsState
|
||||||
lottery: LotteryState
|
lottery: LotteryState
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export const getBalanceAmount = (amount: BigNumber, decimals = 18) => {
|
||||||
*/
|
*/
|
||||||
export const getBalanceNumber = (balance: BigNumber, decimals = 18, decimalPlaces?: number) => {
|
export const getBalanceNumber = (balance: BigNumber, decimals = 18, decimalPlaces?: number) => {
|
||||||
const displayBalance = getBalanceAmount(balance, decimals)
|
const displayBalance = getBalanceAmount(balance, decimals)
|
||||||
return decimalPlaces ?displayBalance.decimalPlaces(decimalPlaces).toNumber():displayBalance.toNumber()
|
return decimalPlaces ? displayBalance.decimalPlaces(decimalPlaces).toNumber() : displayBalance.toNumber()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getFullDisplayBalance = (balance: BigNumber, decimals = 18, displayDecimals?: number) => {
|
export const getFullDisplayBalance = (balance: BigNumber, decimals = 18, displayDecimals?: number) => {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
import React, { useMemo, useState } from 'react'
|
||||||
|
import BigNumber from 'bignumber.js'
|
||||||
|
import styled, { keyframes } from 'styled-components'
|
||||||
|
import { Flex, Text, Skeleton } from '@pancakeswap/uikit'
|
||||||
|
import { provider as ProviderType } from 'web3-core'
|
||||||
|
import { getBoardAddress } from 'utils/addressHelpers'
|
||||||
|
import useI18n from 'hooks/useI18n'
|
||||||
|
import ExpandableSectionButton from 'components/ExpandableSectionButton'
|
||||||
|
import DetailsSection from './DetailsSection'
|
||||||
|
import CardHeading from './CardHeading'
|
||||||
|
import CardActionsContainer from './CardActionsContainer'
|
||||||
|
|
||||||
|
const RainbowLight = keyframes`
|
||||||
|
0% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-position: 100% 50%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const StyledCardAccent = styled.div`
|
||||||
|
background: linear-gradient(
|
||||||
|
45deg,
|
||||||
|
rgba(255, 0, 0, 1) 0%,
|
||||||
|
rgba(255, 154, 0, 1) 10%,
|
||||||
|
rgba(208, 222, 33, 1) 20%,
|
||||||
|
rgba(79, 220, 74, 1) 30%,
|
||||||
|
rgba(63, 218, 216, 1) 40%,
|
||||||
|
rgba(47, 201, 226, 1) 50%,
|
||||||
|
rgba(28, 127, 238, 1) 60%,
|
||||||
|
rgba(95, 21, 242, 1) 70%,
|
||||||
|
rgba(186, 12, 248, 1) 80%,
|
||||||
|
rgba(251, 7, 217, 1) 90%,
|
||||||
|
rgba(255, 0, 0, 1) 100%
|
||||||
|
);
|
||||||
|
background-size: 300% 300%;
|
||||||
|
animation: ${RainbowLight} 2s linear infinite;
|
||||||
|
border-radius: 32px;
|
||||||
|
filter: blur(6px);
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
right: -2px;
|
||||||
|
bottom: -2px;
|
||||||
|
left: -2px;
|
||||||
|
z-index: -1;
|
||||||
|
`
|
||||||
|
|
||||||
|
const FCard = styled.div`
|
||||||
|
align-self: baseline;
|
||||||
|
background: ${(props) => props.theme.card.background};
|
||||||
|
border-radius: 32px;
|
||||||
|
box-shadow: 0px 2px 30px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
padding: 24px;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
`
|
||||||
|
|
||||||
|
const Divider = styled.div`
|
||||||
|
background-color: ${({ theme }) => theme.colors.borderColor};
|
||||||
|
height: 1px;
|
||||||
|
margin: 28px auto;
|
||||||
|
width: 100%;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ExpandingWrapper = styled.div<{ expanded: boolean }>`
|
||||||
|
height: ${(props) => (props.expanded ? '100%' : '0px')};
|
||||||
|
overflow: hidden;
|
||||||
|
`
|
||||||
|
|
||||||
|
interface NodeCardProps {
|
||||||
|
node: any
|
||||||
|
removed: boolean
|
||||||
|
provider?: ProviderType
|
||||||
|
account?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const NodeCard: React.FC<NodeCardProps> = ({ node, account }) => {
|
||||||
|
const TranslateString = useI18n()
|
||||||
|
|
||||||
|
const [showExpandableSection, setShowExpandableSection] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FCard>
|
||||||
|
{/* {true && <StyledCardAccent />} */}
|
||||||
|
<CardHeading name={TranslateString(node.stringId, node.name)} img={node.img} tokenSymbol={node.tokenSymbol} />
|
||||||
|
<CardActionsContainer node={node} account={account} />
|
||||||
|
<Divider />
|
||||||
|
<ExpandableSectionButton
|
||||||
|
onClick={() => setShowExpandableSection(!showExpandableSection)}
|
||||||
|
expanded={showExpandableSection}
|
||||||
|
/>
|
||||||
|
<ExpandingWrapper expanded={showExpandableSection}>
|
||||||
|
<DetailsSection
|
||||||
|
bscScanAddress={`${process.env.REACT_APP_NETWORK_URL}/address/${getBoardAddress()}`}
|
||||||
|
totalValueFormated={node.tokenBalance || '-'}
|
||||||
|
/>
|
||||||
|
</ExpandingWrapper>
|
||||||
|
</FCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NodeCard
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
import React, { useState, useCallback } from 'react'
|
||||||
|
import BigNumber from 'bignumber.js'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { provider as ProviderType } from 'web3-core'
|
||||||
|
import { getAddress } from 'utils/addressHelpers'
|
||||||
|
import { getBep20Contract } from 'utils/contractHelpers'
|
||||||
|
import { Button, Flex, Text } from '@pancakeswap/uikit'
|
||||||
|
import { Boards } from 'state/types'
|
||||||
|
import { useBoardUser } from 'state/hooks'
|
||||||
|
import { TOKEN_SYMBOL2 } from 'config/index'
|
||||||
|
import { getBalanceNumber } from 'utils/formatBalance'
|
||||||
|
import useI18n from 'hooks/useI18n'
|
||||||
|
import useWeb3 from 'hooks/useWeb3'
|
||||||
|
import { useNodeApprove } from 'hooks/useApprove'
|
||||||
|
import UnlockButton from 'components/UnlockButton'
|
||||||
|
import StakeAction from './StakeAction'
|
||||||
|
|
||||||
|
const Action = styled.div`
|
||||||
|
padding-top: 16px;
|
||||||
|
`
|
||||||
|
|
||||||
|
interface NodeCardActionsProps {
|
||||||
|
node: Boards
|
||||||
|
provider?: ProviderType
|
||||||
|
account?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const CardActions: React.FC<NodeCardActionsProps> = ({ node, account }) => {
|
||||||
|
const TranslateString = useI18n()
|
||||||
|
const [requestedApproval, setRequestedApproval] = useState(false)
|
||||||
|
const pid = node.pid
|
||||||
|
const { allowance, tokenBalance, stakedBalance, xCandyBalance } = useBoardUser(pid)
|
||||||
|
const isApproved = account && allowance && allowance.isGreaterThan(0)
|
||||||
|
const web3 = useWeb3()
|
||||||
|
const tokenAddress = getAddress(node.tokenAddresses)
|
||||||
|
const tokenContract = getBep20Contract(tokenAddress, web3)
|
||||||
|
|
||||||
|
const { onApprove } = useNodeApprove(tokenContract)
|
||||||
|
|
||||||
|
const handleApprove = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setRequestedApproval(true)
|
||||||
|
await onApprove()
|
||||||
|
setRequestedApproval(false)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}, [onApprove])
|
||||||
|
|
||||||
|
const renderApprovalOrStakeButton = () => {
|
||||||
|
return isApproved ? (
|
||||||
|
<>
|
||||||
|
<StakeAction stakedBalance={stakedBalance} tokenBalance={tokenBalance} pid={pid} />
|
||||||
|
<Flex flexDirection="column" alignItems="flex-start" mt="10">
|
||||||
|
<Flex>
|
||||||
|
<Text bold textTransform="uppercase" color="secondary" fontSize="12px" pr="3px">
|
||||||
|
{TOKEN_SYMBOL2}
|
||||||
|
</Text>
|
||||||
|
<Text color="textSubtle" fontSize="12px">
|
||||||
|
{TranslateString(101018, 'Amount')}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
<Text bold textTransform="uppercase" fontSize="20px">
|
||||||
|
{getBalanceNumber(new BigNumber(xCandyBalance))}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Button mt="8px" width="100%" disabled={requestedApproval} onClick={handleApprove}>
|
||||||
|
{TranslateString(758, 'Approve Contract')}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Action>
|
||||||
|
<Flex>
|
||||||
|
<Text bold textTransform="uppercase" color="secondary" fontSize="12px" pr="3px">
|
||||||
|
{node.tokenSymbol}
|
||||||
|
</Text>
|
||||||
|
<Text bold textTransform="uppercase" color="textSubtle" fontSize="12px">
|
||||||
|
{TranslateString(101004, 'Staked')}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
{!account ? <UnlockButton mt="8px" width="100%" /> : renderApprovalOrStakeButton()}
|
||||||
|
</Action>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CardActions
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
import React from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { Tag, Flex, Heading, Image } from '@pancakeswap/uikit'
|
||||||
|
import Question from 'components/QuestionHelper'
|
||||||
|
import useI18n from 'hooks/useI18n'
|
||||||
|
|
||||||
|
export interface ExpandableSectionProps {
|
||||||
|
name?: string
|
||||||
|
isCommunityFarm?: boolean
|
||||||
|
img?: string
|
||||||
|
tokenSymbol?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = styled(Flex)`
|
||||||
|
svg {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const CardHeading: React.FC<ExpandableSectionProps> = ({ name, img, tokenSymbol }) => {
|
||||||
|
const TranslateString = useI18n()
|
||||||
|
return (
|
||||||
|
<Wrapper justifyContent="space-between" alignItems="center" mb="12px">
|
||||||
|
<Image src={`/images/nodes/${img}.png`} width={64} height={64} />
|
||||||
|
<Flex>
|
||||||
|
<Heading mb="4px">{name}</Heading>
|
||||||
|
<Question
|
||||||
|
text={TranslateString(101017, 'To join board, you need to stake at least 0.1% total supply of Token')}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Wrapper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CardHeading
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
import React from 'react'
|
||||||
|
import useI18n from 'hooks/useI18n'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { Text, Flex, Link, LinkExternal } from '@pancakeswap/uikit'
|
||||||
|
|
||||||
|
export interface ExpandableSectionProps {
|
||||||
|
bscScanAddress?: string
|
||||||
|
removed?: boolean
|
||||||
|
totalValueFormated?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
margin-top: 24px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const StyledLinkExternal = styled(LinkExternal)`
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: normal;
|
||||||
|
color: ${({ theme }) => theme.colors.text};
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
padding-left: 4px;
|
||||||
|
height: 18px;
|
||||||
|
width: auto;
|
||||||
|
fill: ${({ theme }) => theme.colors.primary};
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const DetailsSection: React.FC<ExpandableSectionProps> = ({ bscScanAddress, removed, totalValueFormated }) => {
|
||||||
|
const TranslateString = useI18n()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<Flex justifyContent="space-between">
|
||||||
|
<Text>{TranslateString(101010, 'Total Staked')}:</Text>
|
||||||
|
<Text>{totalValueFormated}</Text>
|
||||||
|
</Flex>
|
||||||
|
{/* )} */}
|
||||||
|
<Flex justifyContent="flex-start">
|
||||||
|
<Link external href={bscScanAddress} bold={false}>
|
||||||
|
{TranslateString(356, 'View on BscScan')}
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
</Wrapper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailsSection
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
import React, { useMemo } from 'react'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import BigNumber from 'bignumber.js'
|
||||||
|
import { Button, Flex, Heading, IconButton, AddIcon, MinusIcon, useModal } from '@pancakeswap/uikit'
|
||||||
|
import useI18n from 'hooks/useI18n'
|
||||||
|
import { useNodeStake } from 'hooks/useStake'
|
||||||
|
import { useNodeUnstake } from 'hooks/useUnstake'
|
||||||
|
import { NODE_UNLOCK_TIME } from 'config/index'
|
||||||
|
import { getBalanceNumber, getFullDisplayBalance } from 'utils/formatBalance'
|
||||||
|
import { useBoardsFromPid } from 'state/hooks'
|
||||||
|
import useToast from 'hooks/useToast'
|
||||||
|
import DepositModal from '../DepositModal'
|
||||||
|
import WithdrawModal from '../WithdrawModal'
|
||||||
|
|
||||||
|
interface NodeCardActionsProps {
|
||||||
|
stakedBalance?: BigNumber
|
||||||
|
tokenBalance?: BigNumber
|
||||||
|
tokenName?: string
|
||||||
|
pid?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const IconButtonWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
svg {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const StakeAction: React.FC<NodeCardActionsProps> = ({ stakedBalance, tokenBalance, tokenName, pid }) => {
|
||||||
|
const TranslateString = useI18n()
|
||||||
|
const { toastWarning } = useToast()
|
||||||
|
const { onStake } = useNodeStake(pid)
|
||||||
|
const { onUnstake } = useNodeUnstake(pid)
|
||||||
|
const { tokenDecimals = 18, minStakeAmount, userData } = useBoardsFromPid(pid)
|
||||||
|
const rawStakedBalance = getBalanceNumber(stakedBalance, tokenDecimals, 8)
|
||||||
|
const minStakedAmount = useMemo(() => {
|
||||||
|
const minAmount = minStakeAmount - getBalanceNumber(userData.stakedBalance, tokenDecimals)
|
||||||
|
return minAmount < 0 ? new BigNumber(0) : new BigNumber(minAmount)
|
||||||
|
}, [userData, minStakeAmount, tokenDecimals])
|
||||||
|
const [onPresentDeposit] = useModal(
|
||||||
|
<DepositModal
|
||||||
|
max={tokenBalance}
|
||||||
|
tokenDecimals={tokenDecimals}
|
||||||
|
min={minStakedAmount}
|
||||||
|
onConfirm={onStake}
|
||||||
|
tokenName={tokenName}
|
||||||
|
/>,
|
||||||
|
)
|
||||||
|
const [onPresentWithdraw] = useModal(
|
||||||
|
<WithdrawModal max={stakedBalance} tokenDecimals={tokenDecimals} onConfirm={onUnstake} tokenName={tokenName} />,
|
||||||
|
)
|
||||||
|
const handleUnstake = () => {
|
||||||
|
const unstakeDay = new Date().getDay()
|
||||||
|
const unlockTime = userData.unlockTime * 1000
|
||||||
|
// 只能周五解锁 NEED CHANGE
|
||||||
|
if (unstakeDay !== NODE_UNLOCK_TIME || unlockTime > new Date().getTime()) {
|
||||||
|
toastWarning(
|
||||||
|
TranslateString(101012, 'Unlock time'),
|
||||||
|
`${dayjs(unlockTime).format('YYYY-MM-DD HH:mm')} Next Friday`, // NEED CHANGE
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (new BigNumber(userData.stakedBalance).toJSON() !== new BigNumber(userData.xCandyBalance).toJSON()) {
|
||||||
|
toastWarning(TranslateString(100103, 'Hint'), TranslateString(101019, 'Unlocking conditions are not met'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
onUnstake()
|
||||||
|
}
|
||||||
|
const renderStakingButtons = () => {
|
||||||
|
return rawStakedBalance === 0 ? (
|
||||||
|
<Button onClick={onPresentDeposit}>{TranslateString(999, `Stake Token`)}</Button>
|
||||||
|
) : (
|
||||||
|
<IconButtonWrapper>
|
||||||
|
<IconButton variant="tertiary" onClick={handleUnstake} mr="6px">
|
||||||
|
<MinusIcon color="primary" />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton variant="tertiary" onClick={onPresentDeposit}>
|
||||||
|
<AddIcon color="primary" />
|
||||||
|
</IconButton>
|
||||||
|
</IconButtonWrapper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex justifyContent="space-between" alignItems="center">
|
||||||
|
<Heading color={rawStakedBalance === 0 ? 'textDisabled' : 'text'}>{rawStakedBalance}</Heading>
|
||||||
|
{renderStakingButtons()}
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StakeAction
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
import BigNumber from 'bignumber.js'
|
||||||
|
import React, { useCallback, useMemo, useState } from 'react'
|
||||||
|
import { Button, Modal, LinkExternal } from '@pancakeswap/uikit'
|
||||||
|
import ModalActions from 'components/ModalActions'
|
||||||
|
import ModalInput from 'components/ModalInput'
|
||||||
|
import useI18n from 'hooks/useI18n'
|
||||||
|
import { useToast } from 'state/hooks'
|
||||||
|
import { getFullDisplayBalance } from 'utils/formatBalance'
|
||||||
|
|
||||||
|
interface DepositModalProps {
|
||||||
|
min: BigNumber
|
||||||
|
max: BigNumber
|
||||||
|
onConfirm: (amount: string) => void
|
||||||
|
onDismiss?: () => void
|
||||||
|
tokenName?: string
|
||||||
|
tokenDecimals?: number
|
||||||
|
addLiquidityUrl?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const DepositModal: React.FC<DepositModalProps> = ({
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
onConfirm,
|
||||||
|
onDismiss,
|
||||||
|
tokenDecimals = 18,
|
||||||
|
tokenName = '',
|
||||||
|
addLiquidityUrl,
|
||||||
|
}) => {
|
||||||
|
const { toastWarning } = useToast()
|
||||||
|
const [val, setVal] = useState('')
|
||||||
|
const [pendingTx, setPendingTx] = useState(false)
|
||||||
|
const TranslateString = useI18n()
|
||||||
|
const fullBalance = useMemo(() => {
|
||||||
|
return getFullDisplayBalance(max, tokenDecimals)
|
||||||
|
}, [max, tokenDecimals])
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(e: React.FormEvent<HTMLInputElement>) => {
|
||||||
|
setVal(e.currentTarget.value)
|
||||||
|
},
|
||||||
|
[setVal],
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleSelectMax = useCallback(() => {
|
||||||
|
setVal(fullBalance)
|
||||||
|
}, [fullBalance, setVal])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal title={TranslateString(101004, 'Stake tokens')} onDismiss={onDismiss}>
|
||||||
|
<ModalInput
|
||||||
|
value={val}
|
||||||
|
onSelectMax={handleSelectMax}
|
||||||
|
onChange={handleChange}
|
||||||
|
max={fullBalance}
|
||||||
|
symbol={tokenName}
|
||||||
|
addLiquidityUrl={addLiquidityUrl}
|
||||||
|
inputTitle={TranslateString(101004, 'Stake')}
|
||||||
|
/>
|
||||||
|
<ModalActions>
|
||||||
|
<Button variant="secondary" onClick={onDismiss} width="100%">
|
||||||
|
{TranslateString(462, 'Cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
width="100%"
|
||||||
|
disabled={pendingTx || fullBalance === '0' || val === '0'}
|
||||||
|
onClick={async () => {
|
||||||
|
if (Number(val) < min.toNumber()) {
|
||||||
|
toastWarning(
|
||||||
|
TranslateString(100103, 'Hint'),
|
||||||
|
`${TranslateString(101011, 'At least stake ')}${min.toNumber()}`,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setPendingTx(true)
|
||||||
|
await onConfirm(val)
|
||||||
|
setPendingTx(false)
|
||||||
|
onDismiss()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{pendingTx ? TranslateString(488, 'Pending Confirmation') : TranslateString(464, 'Confirm')}
|
||||||
|
</Button>
|
||||||
|
</ModalActions>
|
||||||
|
<LinkExternal href={addLiquidityUrl} style={{ alignSelf: 'center' }}>
|
||||||
|
{TranslateString(100102, 'Get')} {tokenName}
|
||||||
|
</LinkExternal>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DepositModal
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
export default styled.div`
|
||||||
|
background-color: ${({ theme }) => theme.colors.textSubtle};
|
||||||
|
height: 1px;
|
||||||
|
margin: 0 auto 32px;
|
||||||
|
width: 100%;
|
||||||
|
`
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
import BigNumber from 'bignumber.js'
|
||||||
|
import React, { useCallback, useMemo, useState } from 'react'
|
||||||
|
import { Button, Modal } from '@pancakeswap/uikit'
|
||||||
|
import ModalActions from 'components/ModalActions'
|
||||||
|
import ModalInput from 'components/ModalInput'
|
||||||
|
import useI18n from 'hooks/useI18n'
|
||||||
|
import { getFullDisplayBalance } from 'utils/formatBalance'
|
||||||
|
|
||||||
|
interface WithdrawModalProps {
|
||||||
|
max: BigNumber
|
||||||
|
onConfirm: (amount: string) => void
|
||||||
|
onDismiss?: () => void
|
||||||
|
tokenDecimals?: number
|
||||||
|
tokenName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const WithdrawModal: React.FC<WithdrawModalProps> = ({
|
||||||
|
onConfirm,
|
||||||
|
tokenDecimals = 18,
|
||||||
|
onDismiss,
|
||||||
|
max,
|
||||||
|
tokenName = '',
|
||||||
|
}) => {
|
||||||
|
const [val, setVal] = useState('')
|
||||||
|
const [pendingTx, setPendingTx] = useState(false)
|
||||||
|
const TranslateString = useI18n()
|
||||||
|
const fullBalance = useMemo(() => {
|
||||||
|
return getFullDisplayBalance(max, tokenDecimals)
|
||||||
|
}, [max, tokenDecimals])
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(e: React.FormEvent<HTMLInputElement>) => {
|
||||||
|
setVal(e.currentTarget.value)
|
||||||
|
},
|
||||||
|
[setVal],
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleSelectMax = useCallback(() => {
|
||||||
|
setVal(fullBalance)
|
||||||
|
}, [fullBalance, setVal])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal title={TranslateString(101008, 'Unstake tokens')} onDismiss={onDismiss}>
|
||||||
|
<ModalInput
|
||||||
|
onSelectMax={handleSelectMax}
|
||||||
|
onChange={handleChange}
|
||||||
|
value={val}
|
||||||
|
max={fullBalance}
|
||||||
|
symbol={tokenName}
|
||||||
|
inputTitle={TranslateString(588, 'Unstake')}
|
||||||
|
/>
|
||||||
|
<ModalActions>
|
||||||
|
<Button variant="secondary" onClick={onDismiss} width="100%">
|
||||||
|
{TranslateString(462, 'Cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
disabled={pendingTx}
|
||||||
|
onClick={async () => {
|
||||||
|
setPendingTx(true)
|
||||||
|
await onConfirm(val)
|
||||||
|
setPendingTx(false)
|
||||||
|
onDismiss()
|
||||||
|
}}
|
||||||
|
width="100%"
|
||||||
|
>
|
||||||
|
{pendingTx ? TranslateString(488, 'Pending Confirmation') : TranslateString(464, 'Confirm')}
|
||||||
|
</Button>
|
||||||
|
</ModalActions>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WithdrawModal
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
export type TableProps = {
|
||||||
|
data?: TableDataTypes[]
|
||||||
|
selectedFilters?: string
|
||||||
|
sortBy?: string
|
||||||
|
sortDir?: string
|
||||||
|
onSort?: (value: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ColumnsDefTypes = {
|
||||||
|
id: number
|
||||||
|
bold: string
|
||||||
|
normal: string
|
||||||
|
name: string
|
||||||
|
translationId: number
|
||||||
|
sortable: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ScrollBarProps = {
|
||||||
|
ref: string
|
||||||
|
width: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TableDataTypes = {
|
||||||
|
POOL: string
|
||||||
|
APY: string
|
||||||
|
EARNED: string
|
||||||
|
STAKED: string
|
||||||
|
DETAILS: string
|
||||||
|
LINKS: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MobileColumnSchema = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
bold: '',
|
||||||
|
normal: 'Farm',
|
||||||
|
name: 'farm',
|
||||||
|
translationId: 999,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
bold: 'CAKE',
|
||||||
|
normal: 'EARNED',
|
||||||
|
name: 'earned',
|
||||||
|
translationId: 999,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
bold: '',
|
||||||
|
normal: 'APR',
|
||||||
|
name: 'apr',
|
||||||
|
translationId: 999,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
bold: '',
|
||||||
|
normal: 'DETAILS',
|
||||||
|
name: 'details',
|
||||||
|
translationId: 999,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const DesktopColumnSchema: ColumnsDefTypes[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
bold: '',
|
||||||
|
normal: 'Farm',
|
||||||
|
name: 'farm',
|
||||||
|
translationId: 999,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
bold: 'CAKE',
|
||||||
|
normal: 'EARNED',
|
||||||
|
name: 'earned',
|
||||||
|
translationId: 999,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
bold: '',
|
||||||
|
normal: 'APR',
|
||||||
|
name: 'apr',
|
||||||
|
translationId: 999,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
bold: '',
|
||||||
|
normal: 'STAKED',
|
||||||
|
name: 'liquidity',
|
||||||
|
translationId: 999,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
bold: '',
|
||||||
|
normal: 'MULTIPLIER',
|
||||||
|
name: 'multiplier',
|
||||||
|
translationId: 999,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
bold: '',
|
||||||
|
normal: 'DETAILS',
|
||||||
|
name: 'details',
|
||||||
|
translationId: 999,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export enum ViewMode {
|
||||||
|
'TABLE' = 'TABLE',
|
||||||
|
'CARD' = 'CARD',
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
export const useNodeStake = (pid: number) => {
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
const { account } = useWeb3React()
|
||||||
|
const nodeChefContract = useNodechef()
|
||||||
|
const { tokenDecimals = 18 } = useNodesFromPid(pid)
|
||||||
|
const handleStake = useCallback(
|
||||||
|
async (amount: string) => {
|
||||||
|
const txHash = await nodeStake(nodeChefContract, amount, account, tokenDecimals)
|
||||||
|
// dispatch(fetchFarmsPublicDataAsync())
|
||||||
|
// dispatch(fetchFarmUserDataAsync(account))
|
||||||
|
console.info(txHash)
|
||||||
|
},
|
||||||
|
[account, dispatch, nodeChefContract, tokenDecimals],
|
||||||
|
)
|
||||||
|
|
||||||
|
return { onStake: handleStake }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useNodeStake
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react'
|
||||||
|
import { Route, useRouteMatch, useLocation } from 'react-router-dom'
|
||||||
|
import { useDispatch } from 'react-redux'
|
||||||
|
import BigNumber from 'bignumber.js'
|
||||||
|
import { useWeb3React } from '@web3-react/core'
|
||||||
|
import { Image, Heading, RowType, Toggle, Text } from '@pancakeswap/uikit'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import FlexLayout from 'components/Layout/Flex'
|
||||||
|
import Page from 'components/Layout/Page'
|
||||||
|
import { useBoards } from 'state/hooks'
|
||||||
|
import useRefresh from 'hooks/useRefresh'
|
||||||
|
import { fetchBoardUserDataAsync, fetchBoardsPublicDataAsync } from 'state/actions'
|
||||||
|
import useI18n from 'hooks/useI18n'
|
||||||
|
import BoardCard from './components/BoardCard/BoardCard'
|
||||||
|
|
||||||
|
const Header = styled.div`
|
||||||
|
padding: 32px 0px;
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
${({ theme }) => theme.mediaQueries.sm} {
|
||||||
|
padding-left: 24px;
|
||||||
|
padding-right: 24px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const SecondText = styled(Text)`
|
||||||
|
white-space: break-spaces;
|
||||||
|
`
|
||||||
|
const Farms: React.FC = () => {
|
||||||
|
const TranslateString = useI18n()
|
||||||
|
const boardsList = useBoards()
|
||||||
|
const [query, setQuery] = useState('')
|
||||||
|
const { account } = useWeb3React()
|
||||||
|
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
const { fastRefresh } = useRefresh()
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(fetchBoardsPublicDataAsync())
|
||||||
|
if (account) {
|
||||||
|
dispatch(fetchBoardUserDataAsync(account))
|
||||||
|
}
|
||||||
|
}, [account, dispatch, fastRefresh])
|
||||||
|
|
||||||
|
const farmsList = useMemo(() => {
|
||||||
|
return boardsList.map((board) => {
|
||||||
|
return board
|
||||||
|
})
|
||||||
|
}, [boardsList])
|
||||||
|
const renderContent = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FlexLayout>
|
||||||
|
{farmsList.map((board) => (
|
||||||
|
<BoardCard key={board.pid} board={board} account={account} removed />
|
||||||
|
))}
|
||||||
|
</FlexLayout>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header>
|
||||||
|
<Heading as="h1" size="xl" color="text" mb="10px" mt="10px">
|
||||||
|
{TranslateString(100004, 'Boards')}
|
||||||
|
</Heading>
|
||||||
|
<SecondText fontSize="28px" color="text">
|
||||||
|
{TranslateString(
|
||||||
|
101013,
|
||||||
|
'Joining the board of directors will obtain the governance token xcandy \n participate in the governance of the project, vote, obtain additional pledge income, \n and have a higher invitation airdrop reward',
|
||||||
|
)}
|
||||||
|
</SecondText>
|
||||||
|
</Header>
|
||||||
|
<Page>{renderContent()}</Page>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Farms
|
||||||
Loading…
Reference in New Issue