diff --git a/public/locales/zh-CN.json b/public/locales/zh-CN.json index 1df6bc0..6e80e7a 100644 --- a/public/locales/zh-CN.json +++ b/public/locales/zh-CN.json @@ -1118,7 +1118,7 @@ "By using the invitation at the top right of the page, new users can be invited to enter and obtained after users purchase coins": "使用页面右上方的邀请,可邀请新用户进入,并在用户购买币后获得", "The commission": "的提成!", "footer %number% text": "使用页面右上方的邀请,可邀请新用户进入,并在用户购买币后获得 %number% 的提成!", - "market value": "市值", + "market value": "市值(24h)", "Loaded all": "已加载全部", "Insufficient Balance": "余额不足", "HCC Currency amount": "HCC币总量", @@ -1153,5 +1153,11 @@ "gross": "总量", "remaining quantity": "剩余数量", "Immediately change": "立即兑换", - "IDO Exchange": "IDO 兑换" + "IDO Exchange": "IDO 兑换", + "Get": "领取", + "IDO Get": "IDO 领取", + "Estimated time of collection": "预计领取时间", + "amount": "金额", + "Change the end": "兑换结束", + "After purchase, it is expected to be available for collection in %time% time. Do you confirm the purchase": "购买后,预计%time%时间后可进行领取,是否确认购买" } diff --git a/src/App.tsx b/src/App.tsx index db999e0..28f686b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -54,7 +54,7 @@ const Referral = lazy(() => import('./views/Referral')) const Board = lazy(() => import('./views/Board')) const Nft = lazy(() => import('./views/Nft')) const Announcement = lazy(() => import('./views/Announcement')) -const Exchange = lazy(() => import('./views/Exchange')) +const Ido = lazy(() => import('./views/Ido')) // This config is required for number formatting BigNumber.config({ @@ -100,8 +100,8 @@ const App: React.FC = () => { - - + + diff --git a/src/components/Menu/config.ts b/src/components/Menu/config.ts index 4c4874d..de4f67d 100644 --- a/src/components/Menu/config.ts +++ b/src/components/Menu/config.ts @@ -28,7 +28,7 @@ const config: (t: ContextApi['t']) => MenuEntry[] = (t) => [ { label: t('IDO Exchange'), icon: 'FarmIcon', - href: '/exchange', + href: '/Ido', }, { label: t('Exchange'), diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index 3f604f6..3d6d002 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -57,7 +57,6 @@ const Menu = (props) => { sign() } }, [unActiveAccount, hasWalletLogin, library]) - useEffect(() => { getDetails() }, []) @@ -72,7 +71,7 @@ const Menu = (props) => { currentLang={currentLanguage.code} langs={languageList} setLang={setLanguage} - cakePriceUsd={hccPriceUsdt.toNumber()} + cakePriceUsd={hccPriceUsdt} links={config(t)} socialLink={socialLink} {...props} diff --git a/src/config/abi/idoPurchase.json b/src/config/abi/idoPurchase.json new file mode 100644 index 0000000..9d7a8b5 --- /dev/null +++ b/src/config/abi/idoPurchase.json @@ -0,0 +1,178 @@ +[ + { + "inputs": [{ "internalType": "contract IERC20", "name": "_HCC", "type": "address" }], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "round", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "total", "type": "uint256" }, + { "indexed": false, "internalType": "address", "name": "token", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "price", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "beginTime", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "endTime", "type": "uint256" } + ], + "name": "AddRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "Harvest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint256", "name": "newLockTime", "type": "uint256" }], + "name": "SetLockTimeEvent", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "round", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "total", "type": "uint256" }, + { "indexed": false, "internalType": "address", "name": "token", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "price", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "beginTime", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "endTime", "type": "uint256" } + ], + "name": "SetRound", + "type": "event" + }, + { + "inputs": [], + "name": "HCC", + "outputs": [{ "internalType": "contract IERC20", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "round", "type": "uint256" }, + { "internalType": "uint256", "name": "total", "type": "uint256" }, + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "price", "type": "uint256" }, + { "internalType": "uint256", "name": "beginTime", "type": "uint256" }, + { "internalType": "uint256", "name": "endTime", "type": "uint256" } + ], + "name": "addRound", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "round", "type": "uint256" }], + "name": "getRound", + "outputs": [ + { "internalType": "uint256", "name": "total", "type": "uint256" }, + { "internalType": "uint256", "name": "remaining", "type": "uint256" }, + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "price", "type": "uint256" }, + { "internalType": "uint256", "name": "beginTime", "type": "uint256" }, + { "internalType": "uint256", "name": "endTime", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "harvest", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "lockTime", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "round", "type": "uint256" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "purchase", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "purchaseInfo", + "outputs": [ + { "internalType": "uint256", "name": "total", "type": "uint256" }, + { "internalType": "uint256", "name": "purchasedAmount", "type": "uint256" }, + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "price", "type": "uint256" }, + { "internalType": "uint256", "name": "beginTime", "type": "uint256" }, + { "internalType": "uint256", "name": "endTime", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "round", "type": "uint256" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "purchaseWithLock", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "uint256", "name": "_lockTime", "type": "uint256" }], + "name": "setLockTime", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [], "name": "setPause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { "internalType": "uint256", "name": "round", "type": "uint256" }, + { "internalType": "uint256", "name": "total", "type": "uint256" }, + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "price", "type": "uint256" }, + { "internalType": "uint256", "name": "beginTime", "type": "uint256" }, + { "internalType": "uint256", "name": "endTime", "type": "uint256" } + ], + "name": "setRound", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "userInfo", + "outputs": [{ "internalType": "uint256", "name": "purchasedAmount", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/config/constants/contracts.ts b/src/config/constants/contracts.ts index 2a752c6..5db9f27 100644 --- a/src/config/constants/contracts.ts +++ b/src/config/constants/contracts.ts @@ -8,11 +8,11 @@ export default { 56: '0x6ab8463a4185b80905e05a9ff80a2d6b714b9e95', }, boardChef: { - 97: '0x126935cae1988efbf8e310abe6be1f9046eaf0ac', + 97: '0x4ECC687D67138694729433e77cD95eEE9a858E40', 56: '0xD34871F12ace1BB8034E18009104b9dA60B84250', // NEED CHANGE 节点董事会合约 }, boardRewardChef: { - 97: '0x2937a050705009270c9b5bc096d57d519ab7c39b', + 97: '0xbCb980b6A4CD67d81B63B0bFA734B4116B218700', 56: '0xD34871F12ace1BB8034E18009104b9dA60B84250', // NEED CHANGE 节点董事会分红合约 }, holderChef: { @@ -20,16 +20,20 @@ export default { 56: '0xD34871F12ace1BB8034E18009104b9dA60B84250', // NEED CHANGE 持币人 }, holderRewardChef: { - 97: '0x46271393dd6f2c8798a44f857888aa6a85af3527', + 97: '0x61FB526924c6BAC9A08E146Da103B19c0DFA1899', 56: '0x46271393dd6f2c8798a44f857888aa6a85af3527', // NEED CHANGE 持币人分红 }, referralChef: { - 97: '0x8a5dc1e8262d6a3de664624fdc13a533ba64e60d', - 56: '0x88F46EF2Ee08494D84942DCA3bd24cDEf7C88Ae2', // NEED CHANGE 邀请或则军团长 + 97: '0xe94282DA5166AD3FEB82F7aE299b2D5DFDF392Ae', + 56: '0xf42D1e1883C2FAA058dfa0D301556EB2d964859a', // NEED CHANGE 邀请或则军团长 }, referralRewardChef: { - 97: '0x86a510e82aceb27ed9e9880fb981d4b42ff16cb4', - 56: '0x88F46EF2Ee08494D84942DCA3bd24cDEf7C88Ae2', // NEED CHANGE 邀请或则军团长收益 + 97: '0xf42D1e1883C2FAA058dfa0D301556EB2d964859a', + 56: '0xf42D1e1883C2FAA058dfa0D301556EB2d964859a', // NEED CHANGE 邀请或则军团长收益 + }, + idoPurchase: { + 97: '0xCCFD5B33774a1568A322FCa262D3378Ff8CcdeCB', + 56: '0xCCFD5B33774a1568A322FCa262D3378Ff8CcdeCB', // NEED CHANGE IDO兑换 }, lotteryV2: { 97: '0x5790c3534F30437641541a0FA04C992799602998', diff --git a/src/config/constants/tokens.ts b/src/config/constants/tokens.ts index 4d39671..6dcd817 100644 --- a/src/config/constants/tokens.ts +++ b/src/config/constants/tokens.ts @@ -73,7 +73,7 @@ const tokens = { symbol: 'HCC', address: { 56: '0x20de22029ab63cf9A7Cf5fEB2b737Ca1eE4c82A6', - 97: '0x77f2efa78c1c2798ad3c753330aa4e1babcaeff8', + 97: '0xE7619D544bD06f4d7362c8aA06a4ca0Ea73ce8c2', }, decimals: 18, projectLink: 'https://tranchess.com/', diff --git a/src/config/localization/translations.json b/src/config/localization/translations.json index 29721e2..a11b707 100644 --- a/src/config/localization/translations.json +++ b/src/config/localization/translations.json @@ -1247,7 +1247,7 @@ "By using the invitation at the top right of the page, new users can be invited to enter and obtained after users purchase coins": "By using the invitation at the top right of the page, new users can be invited to enter and obtained after users purchase coins", "The commission": "The commission!", "footer %number% text": "By using the invitation at the top right of the page, new users can be invited to enter and obtained after users purchase coins %number% The commission!", - "market value": "market value", + "market value": "market value(24h)", "Loaded all": "Loaded all", "HCC Currency amount": "HCC Currency amount", "Lock up time": "Lock up time", @@ -1280,5 +1280,11 @@ "gross": "gross", "remaining quantity": "remaining quantity", "Immediately change": "Immediately change", - "IDO Exchange": "IDO Exchange" + "IDO Exchange": "IDO Exchange", + "Get": "Get", + "IDO Get": "IDO Get", + "Estimated time of collection": "Estimated time of collection", + "amount": "amount", + "Change the end": "Change the end", + "After purchase, it is expected to be available for collection in %time% time. Do you confirm the purchase": "After purchase, it is expected to be available for collection in %time% time. Do you confirm the purchase" } diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts index 2e9ab77..03e5c29 100644 --- a/src/hooks/useContract.ts +++ b/src/hooks/useContract.ts @@ -27,6 +27,7 @@ import { getReferralchefContract, getBoardchefContract, getReferralRewardchefContract, + getIdoPurchaseContract, } from 'utils/contractHelpers' // Imports below migrated from Exchange useContract.ts @@ -109,6 +110,10 @@ export const useReferralchef = () => { const { library } = useActiveWeb3React() return useMemo(() => getReferralchefContract(library.getSigner()), [library]) } +export const useIdoPurchase = () => { + const { library } = useActiveWeb3React() + return useMemo(() => getIdoPurchaseContract(library.getSigner()), [library]) +} export const useReferralRewardchef = () => { const { library } = useActiveWeb3React() return useMemo(() => getReferralRewardchefContract(library.getSigner()), [library]) diff --git a/src/services/idoPurchase.ts b/src/services/idoPurchase.ts new file mode 100644 index 0000000..70792fe --- /dev/null +++ b/src/services/idoPurchase.ts @@ -0,0 +1,10 @@ +import request from 'utils/request' + +export const getPurchaseActivity = () => { + return request.request({ + url: '/high_city/app/api/purchase/activity/current', + method: 'get', + }) +} + +export default getPurchaseActivity diff --git a/src/state/hooks.ts b/src/state/hooks.ts index ddc2d21..729c553 100644 --- a/src/state/hooks.ts +++ b/src/state/hooks.ts @@ -373,9 +373,12 @@ export const usePriceCakeBusd = (): BigNumber => { const cakeBnbFarm = useFarmFromPid(251) return new BigNumber(cakeBnbFarm.token.busdPrice) } -export const usePriceHccUsdt = (): BigNumber => { +export const usePriceHccUsdt = (): number => { const hccUsdtFarm = useFarmFromPid(1000) // NEED CHANGE - return new BigNumber(hccUsdtFarm.token.busdPrice) + return new BigNumber(hccUsdtFarm.token.busdPrice).toString() || !hccUsdtFarm.token.busdPrice + ? 0 + : new BigNumber(hccUsdtFarm.token.busdPrice).toNumber() + // return new BigNumber(hccUsdtFarm.token.busdPrice) } // Block diff --git a/src/state/ido/index.ts b/src/state/ido/index.ts new file mode 100644 index 0000000..b682be9 --- /dev/null +++ b/src/state/ido/index.ts @@ -0,0 +1,21 @@ +import { createSlice } from '@reduxjs/toolkit' +import BigNumber from 'bignumber.js' +import erc20ABI from 'config/abi/erc20.json' +import multicall from 'utils/multicall' +import tokens from 'config/constants/tokens' +import { getAddress, getIdoPurchaseAddress } from 'utils/addressHelpers' + +export const fetchIdoUserAllowances = async (account: string) => { + const tokenAddresses = getAddress(tokens.usdt.address) + const idoPurchaseAddress = getIdoPurchaseAddress() + const calls = [{ address: tokenAddresses, name: 'allowance', params: [account, idoPurchaseAddress] }] + const rawLpAllowances = await multicall(erc20ABI, calls) + const parsedLpAllowances = rawLpAllowances.map((balance) => { + return new BigNumber(balance).toNumber() + }) + return { + usdt: parsedLpAllowances[0], + } +} + +export default fetchIdoUserAllowances diff --git a/src/utils/addressHelpers.ts b/src/utils/addressHelpers.ts index 30cb2ea..7f58f0d 100644 --- a/src/utils/addressHelpers.ts +++ b/src/utils/addressHelpers.ts @@ -74,3 +74,6 @@ export const getBunnySpecialCakeVaultAddress = () => { export const getBunnySpecialPredictionAddress = () => { return getAddress(addresses.bunnySpecialPrediction) } +export const getIdoPurchaseAddress = () => { + return getAddress(addresses.idoPurchase) +} diff --git a/src/utils/contractHelpers.ts b/src/utils/contractHelpers.ts index 3e3abbe..d409630 100644 --- a/src/utils/contractHelpers.ts +++ b/src/utils/contractHelpers.ts @@ -27,6 +27,7 @@ import { getBoardAddress, getReferralAddress, getReferralRewardAddress, + getIdoPurchaseAddress, } from 'utils/addressHelpers' // ABI @@ -59,6 +60,7 @@ import chainlinkOracleAbi from 'config/abi/chainlinkOracle.json' import MultiCallAbi from 'config/abi/Multicall.json' import bunnySpecialCakeVaultAbi from 'config/abi/bunnySpecialCakeVault.json' import bunnySpecialPredictionAbi from 'config/abi/bunnySpecialPrediction.json' +import idoPurchase from 'config/abi/idoPurchase.json' import { ChainLinkOracleContract, PredictionsContract } from './types' const getContract = (abi: any, address: string, signer?: ethers.Signer | ethers.providers.Provider) => { @@ -127,6 +129,9 @@ export const getReferralchefContract = (signer?: ethers.Signer | ethers.provider export const getReferralRewardchefContract = (signer?: ethers.Signer | ethers.providers.Provider) => { return getContract(referralRewardChef, getReferralRewardAddress(), signer) } +export const getIdoPurchaseContract = (signer?: ethers.Signer | ethers.providers.Provider) => { + return getContract(idoPurchase, getIdoPurchaseAddress(), signer) +} export const getClaimRefundContract = (signer?: ethers.Signer | ethers.providers.Provider) => { return getContract(claimRefundAbi, getClaimRefundAddress(), signer) } diff --git a/src/utils/formatBalance.ts b/src/utils/formatBalance.ts index c5f31e5..fa8d632 100644 --- a/src/utils/formatBalance.ts +++ b/src/utils/formatBalance.ts @@ -65,6 +65,6 @@ export const formatFixedNumber = (number: ethers.FixedNumber, displayDecimals = return formatBigNumber(ethers.BigNumber.from(leftSide), displayDecimals, decimals) } -export const formatDivNumber = (number: number, decimals = 4) => { +export const formatDivNumber = (number: BigNumber | number, decimals = 4) => { return new BigNumber(number).div(BIG_TEN.pow(decimals)).toNumber() } diff --git a/src/views/Board/components/BoardCard/BoardCard.tsx b/src/views/Board/components/BoardCard/BoardCard.tsx index 2902e55..b2a5316 100644 --- a/src/views/Board/components/BoardCard/BoardCard.tsx +++ b/src/views/Board/components/BoardCard/BoardCard.tsx @@ -119,7 +119,7 @@ const BoardCard: React.FC = ({ board, account, boardsData }) => { const date2 = dayjs(date) const time = date2.diff(date1) if (time > 0) { - const hour = Math.floor((time / (1000 * 60 * 60)) % 24) + const hour = Math.floor(time / (1000 * 60 * 60)) const minute = Math.floor((time / (1000 * 60)) % 60) const second = Math.round((time / 1000) % 60) setCountDown(`${hour}:${minute}:${second}`) diff --git a/src/views/Board/components/BoardCard/CardActionsContainer.tsx b/src/views/Board/components/BoardCard/CardActionsContainer.tsx index 77c3136..135293b 100644 --- a/src/views/Board/components/BoardCard/CardActionsContainer.tsx +++ b/src/views/Board/components/BoardCard/CardActionsContainer.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, useMemo } from 'react' +import React, { useState, useCallback, useMemo, useEffect } from 'react' import BigNumber from 'bignumber.js' import styled from 'styled-components' import { provider as ProviderType } from 'web3-core' @@ -76,7 +76,7 @@ const CardActions: React.FC = ({ board, account }) => { {displayBalance} - {(Number(hccPriceUsdt.toNumber().toFixed(3)) * Number(displayBalance)).toFixed(3)} USDT + {hccPriceUsdt ? (hccPriceUsdt * Number(displayBalance)).toFixed(3) : 0} USDT diff --git a/src/views/Exchange/components/ExchangeCard.tsx b/src/views/Exchange/components/ExchangeCard.tsx deleted file mode 100644 index 67a2c1a..0000000 --- a/src/views/Exchange/components/ExchangeCard.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, { useMemo, useState, useEffect } from 'react' -import styled, { keyframes } from 'styled-components' -import { useTranslation } from 'contexts/Localization' -import { Flex, Text, Button } from '@pancakeswap/uikit' - -import { useAccount } from 'state/userInfo/hooks' -import UnlockButton from 'components/UnlockButton' -import ExchangeInput from './ExchangeInput' - -const FCard = styled.div` - width: 650px; - 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; - position: relative; - text-align: center; - padding: 50px 70px; -` -const HeaderText = styled(Text)` - font-size: 48px; - color: #280d5f; -` -const TimeText = styled(Text)` - color: #7a6eaa; - font-size: 22px; -` - -const RateText = styled(Text)` - font-size: 30px; - color: #280d5f; -` -const RateNumber = styled(Text)` - font-size: 30px; - color: #1fc7d4; -` -const FooterButton = styled.div` - margin-top: 50px; - width: 100%; -` -const UnlockButtonDiv = styled(UnlockButton)` - width: 100%; -` - -const ExchangeCard: React.FC = () => { - const { t } = useTranslation() - - const account = useAccount() - - return ( - - {t('IDO exchange in the first phase')} - {t('Opening time of next exchange period:')}2022.5.16 00:00 - - - {t('Exchange rate')}: - 1USDT=100HCC - - - {t('gross')}:100000HCC - - {t('remaining quantity')}:100000HCC - - - - - {account ? {t('Immediately change')} : } - - - ) -} - -export default ExchangeCard diff --git a/src/views/Exchange/index.tsx b/src/views/Exchange/index.tsx deleted file mode 100644 index e4ec50c..0000000 --- a/src/views/Exchange/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React, { useState, useEffect } from 'react' -import styled from 'styled-components' -import ExchangeCard from './components/ExchangeCard' - -const PageContent = styled.div` - min-height: calc(100vh - 64px); - background-image: url('/images/recommend/bg.svg'); - background-size: cover; - background-repeat: no-repeat; - display: flex; - align-items: center; - justify-content: center; -` - -const Exchange: React.FC = () => { - return ( - - - - ) -} -export default Exchange diff --git a/src/views/Home/components/FristCom.tsx b/src/views/Home/components/FristCom.tsx index 1cc5179..8e4cc20 100644 --- a/src/views/Home/components/FristCom.tsx +++ b/src/views/Home/components/FristCom.tsx @@ -201,7 +201,7 @@ const FristCom: React.FC = () => { - + {/* {burned.map((item) => { return })} */} diff --git a/src/views/Ido/components/BuyModal.tsx b/src/views/Ido/components/BuyModal.tsx new file mode 100644 index 0000000..91c11ca --- /dev/null +++ b/src/views/Ido/components/BuyModal.tsx @@ -0,0 +1,37 @@ +import BigNumber from 'bignumber.js' +import React, { useCallback, useMemo, useState } from 'react' +import { Button, Modal, Text } from '@pancakeswap/uikit' +import { ModalActions, ModalInput } from 'components/Modal' +import { useTranslation } from 'contexts/Localization' +import useToast from 'hooks/useToast' +import { getFullDisplayBalance } from 'utils/formatBalance' + +interface AffirmModalProps { + title?: string + content?: string + onDismiss?: () => void + handSubmit?: () => void +} + +const BuyModal: React.FC = ({ title, content, onDismiss, handSubmit }) => { + const { t } = useTranslation() + const submit = async () => { + await handSubmit() + onDismiss() + } + return ( + + {!content ? t('Whether to cancel') : content} + + + {t('Cancel')} + + + {t('Confirm')} + + + + ) +} + +export default BuyModal diff --git a/src/views/Ido/components/GetCard.tsx b/src/views/Ido/components/GetCard.tsx new file mode 100644 index 0000000..5c6b3a2 --- /dev/null +++ b/src/views/Ido/components/GetCard.tsx @@ -0,0 +1,79 @@ +import React, { useMemo, useState, useEffect, useCallback } from 'react' +import dayjs from 'dayjs' +import styled, { keyframes } from 'styled-components' +import { useTranslation } from 'contexts/Localization' +import { Flex, Text, Button, Input } from '@pancakeswap/uikit' +import { useHarvest } from '../hooks' + +interface GetCardProps { + price?: number | string + time?: number | string +} + +const FCard = styled.div` + width: 434px; + 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; + position: relative; + text-align: left; + padding: 27px 20px; + margin-top: 50px; +` + +const ExchangeCard: React.FC = ({ price = 0, time = 0 }) => { + const { t } = useTranslation() + + // const account = useAccount() + // const lockTime = useLockTime() + // const [time, setTime] = useState(0) + // const userInfo = useUserInfo() + // const [price, setPrice] = useState(0) + // const init = async () => { + // const data = await lockTime() + // console.log(data) + // setTime(data) + // const info = await userInfo(account) + // console.log(info) + // setPrice(info) + // } + // useEffect(() => { + // if (account) { + // init() + // } + // }, [account]) + const harvest = useHarvest() + const getPrice = async () => { + const res = await harvest() + console.log(res) + } + + return ( + + {/* 锁仓时间大于当前时间 不可领取 */} + {/* 锁仓时间小于当前时间 可领取 */} + {/* 当前时间大于锁仓时间 不显示 */} + + + {t('IDO Get')} + {new Date().getTime() <= time && ( + + {t('Estimated time of collection')}:{dayjs(time).format('YYYY-MM-DD HH:mm:ss')} + + )} + + {t('amount')}: + {price}HCC + + + new Date().getTime() || !price} onClick={getPrice}> + {t('Get')} + + + + ) +} + +export default ExchangeCard diff --git a/src/views/Ido/components/HeaderStatus.tsx b/src/views/Ido/components/HeaderStatus.tsx new file mode 100644 index 0000000..955b99c --- /dev/null +++ b/src/views/Ido/components/HeaderStatus.tsx @@ -0,0 +1,40 @@ +import React from 'react' +import dayjs from 'dayjs' +import styled from 'styled-components' +import { useTranslation } from 'contexts/Localization' +import { Text } from '@pancakeswap/uikit' + +const TimeText = styled(Text)` + color: #7a6eaa; + font-size: 22px; +` +interface RoundDetailProps { + beginTime?: number + endTime?: number + price?: number + remaining?: number + token?: string + total?: number +} +interface HeaderStatusProps { + status?: string + roundDetail?: RoundDetailProps +} + +const HeaderStatus: React.FC = ({ status, roundDetail }) => { + const { t } = useTranslation() + + return ( + + {status === 'none' && t('Change the end')} + {status === 'proceed' && + `${dayjs(roundDetail?.beginTime).format('YYYY-MM-DD HH:mm:ss')} ~ ${dayjs(roundDetail?.endTime).format( + 'YYYY-MM-DD HH:mm:ss', + )}`} + {status === 'end' && + `${t('Opening time of next exchange period:')}${dayjs(roundDetail?.beginTime).format('YYYY-MM-DD HH:mm:ss')}`} + + ) +} + +export default HeaderStatus diff --git a/src/views/Exchange/components/ExchangeInput.tsx b/src/views/Ido/components/IdoInput.tsx similarity index 64% rename from src/views/Exchange/components/ExchangeInput.tsx rename to src/views/Ido/components/IdoInput.tsx index 89e28d9..35ef1c4 100644 --- a/src/views/Exchange/components/ExchangeInput.tsx +++ b/src/views/Ido/components/IdoInput.tsx @@ -19,18 +19,26 @@ const CoinText = styled(Text)` margin-bottom: 20px; ` -interface InputProps { +interface ExchangeInputProps { name: string value?: number | string + disabled?: boolean + onChange: (e: React.FormEvent) => void } -const ExchangeInput: React.FC = ({ name, value }) => { +const ExchangeInput: React.FC = ({ name, value, onChange, disabled = true }) => { const { t } = useTranslation() return ( {name} - + ) } diff --git a/src/views/Ido/components/IdoPurchaseCard.tsx b/src/views/Ido/components/IdoPurchaseCard.tsx new file mode 100644 index 0000000..de1625d --- /dev/null +++ b/src/views/Ido/components/IdoPurchaseCard.tsx @@ -0,0 +1,191 @@ +import React, { useState, useEffect } from 'react' +import dayjs from 'dayjs' +import styled from 'styled-components' +import BigNumber from 'bignumber.js' +import { useTranslation } from 'contexts/Localization' +import { Text, Button, useModal } from '@pancakeswap/uikit' +import { getAddress } from 'utils/addressHelpers' +import { getPurchaseActivity } from 'services/idoPurchase' +import { fetchIdoUserAllowances } from 'state/ido' +import { useERC20 } from 'hooks/useContract' +import useToast from 'hooks/useToast' +import { getDecimalAmountNumber, formatDivNumber } from 'utils/formatBalance' + +import tokens from 'config/constants/tokens' +import { useAccount } from 'state/userInfo/hooks' +import UnlockButton from 'components/UnlockButton' +import ExchangeInput from './IdoInput' +import HeaderStatus from './HeaderStatus' +import RateText from './RateText' + +import BuyModal from './BuyModal' +import { useApproveIdo, useBuyTransaction, useCheckTokenBalance } from '../hooks' + +const FCard = styled.div` + width: 650px; + 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; + position: relative; + text-align: center; + padding: 50px 70px; + ${({ theme }) => theme.mediaQueries.xs} { + width: 350px; + padding: 20px 30px; + } + ${({ theme }) => theme.mediaQueries.lg} { + width: 650px; + padding: 50px 70px; + } +` +const HeaderText = styled(Text)` + font-size: 48px; + color: #280d5f; +` +const FooterButton = styled.div` + margin-top: 50px; + width: 100%; +` +const UnlockButtonDiv = styled(UnlockButton)` + width: 100%; +` +interface DetailProps { + id?: string + name?: string + beginTime?: string + endTime?: string +} +interface RoundDetailProps { + beginTime?: number + endTime?: number + price?: number + remaining?: number + token?: string + total?: number +} +interface Props { + status?: string + roundDetail?: RoundDetailProps + time?: number +} + +const ExchangeCard: React.FC = ({ status, roundDetail, time }) => { + const { t } = useTranslation() + const { toastSuccess, toastError } = useToast() + const account = useAccount() + + const [allowanceList, setAllowanceList] = useState({ usdt: 0 }) + const [loading, setLoading] = useState(false) + const getAllowances = async () => { + const allowances = await fetchIdoUserAllowances(account) + setAllowanceList({ + usdt: allowances.usdt, + }) + } + const [idoInfo, setIdoInfo] = useState({}) + const getPurchaseInfo = async () => { + const data = await getPurchaseActivity() + setIdoInfo(data) + } + + useEffect(() => { + if (account) { + getAllowances() + getPurchaseInfo() + } + }, [account]) + + const usdtContract = useERC20(getAddress(tokens.usdt.address)) + const { onApprove: onUsdtApprove } = useApproveIdo(usdtContract) + + const handleApprove = async (approve) => { + try { + setLoading(true) + await approve() + setLoading(false) + getAllowances() + } catch (e) { + console.error(e) + } + } + + const [usdtPrice, setUsdtPrice] = useState('') + const [hccPrice, setHccPrice] = useState('') + + const handleUsdtChange = (e: React.FormEvent) => { + const price = new BigNumber(e.currentTarget.value).multipliedBy(roundDetail?.price) + setUsdtPrice(e.currentTarget.value) + e.currentTarget.value ? setHccPrice(price.toString()) : setHccPrice('') + } + const handleHccChange = (e: React.FormEvent) => { + const price = new BigNumber(e.currentTarget.value).div(roundDetail?.price).toNumber() + setHccPrice(e.currentTarget.value) + e.currentTarget.value ? setUsdtPrice(price.toString()) : setUsdtPrice('') + } + const buyTransaction = useBuyTransaction() + const [onCheckBalance] = useCheckTokenBalance() + // 立即兑换 + const immediatelyChange = async () => { + try { + const enoughBalance = onCheckBalance(usdtPrice) + if (enoughBalance) { + const res = await buyTransaction(hccPrice) + } + } catch (e: any) { + toastError(e?.error?.message || e.message) + } + } + + const [onPresentAffirm] = useModal( + , + ) + + return ( + + {idoInfo?.name} + + + + + + {account ? ( + allowanceList.usdt ? ( + time ? ( + + {t('Immediately change')} + + ) : ( + + {t('Immediately change')} + + ) + ) : ( + { + handleApprove(onUsdtApprove) + }} + > + {t('Approve %coin% Contract', { coin: 'USDT' })} + + ) + ) : ( + + )} + + + ) +} + +export default ExchangeCard diff --git a/src/views/Ido/components/RateText.tsx b/src/views/Ido/components/RateText.tsx new file mode 100644 index 0000000..c48a132 --- /dev/null +++ b/src/views/Ido/components/RateText.tsx @@ -0,0 +1,75 @@ +import React from 'react' +import styled from 'styled-components' +import { useTranslation } from 'contexts/Localization' +import { Flex, Text } from '@pancakeswap/uikit' + +const RateText = styled(Text)` + font-size: 30px; + color: #280d5f; + ${({ theme }) => theme.mediaQueries.xs} { + font-size: 25px; + } + ${({ theme }) => theme.mediaQueries.lg} { + font-size: 30px; + } +` +const RateNumber = styled(Text)` + font-size: 30px; + color: #1fc7d4; + ${({ theme }) => theme.mediaQueries.xs} { + font-size: 25px; + } + ${({ theme }) => theme.mediaQueries.lg} { + font-size: 30px; + } +` +const FlexRowCenter = styled(Flex)` + justify-content: center; + align-items: center; +` +const FlexCenter = styled(Flex)` + justify-content: center; + align-items: center; + + ${({ theme }) => theme.mediaQueries.xs} { + flex-direction: column; + } + ${({ theme }) => theme.mediaQueries.lg} { + flex-direction: row; + } +` +interface RoundDetailProps { + beginTime?: number + endTime?: number + price?: number + remaining?: number + token?: string + total?: number +} +interface Props { + status?: string + roundDetail?: RoundDetailProps +} + +const RateCom: React.FC = ({ status, roundDetail }) => { + const { t } = useTranslation() + + return ( + <> + + {t('Exchange rate')}: + {status === 'none' ? '-------' : `1USDT=${roundDetail?.price}HCC`} + + + + {t('gross')}:{status === 'none' ? ' -------- ' : `${roundDetail?.total}HCC`} + + + {t('remaining quantity')}:{status === 'none' ? ' -------- ' : `${roundDetail?.remaining}HCC`} + + + > + ) +} + +export default RateCom diff --git a/src/views/Ido/hooks/index.ts b/src/views/Ido/hooks/index.ts new file mode 100644 index 0000000..3bed001 --- /dev/null +++ b/src/views/Ido/hooks/index.ts @@ -0,0 +1,138 @@ +import { useCallback } from 'react' +import { useIdoPurchase, useReferralchef } from 'hooks/useContract' +import useTokenBalance from 'hooks/useTokenBalance' +import idoPurchaseABI from 'config/abi/idoPurchase.json' +import { getAddress, getIdoPurchaseAddress } from 'utils/addressHelpers' +import { getPurchaseActivity } from 'services/idoPurchase' +import BigNumber from 'bignumber.js' +import multicall from 'utils/multicall' +import tokensList from 'config/constants/tokens' +import useToast from 'hooks/useToast' +import { useTranslation } from 'contexts/Localization' +import { ethers, Contract } from 'ethers' +import { getBalanceNumber, getDecimalAmount, formatDivNumber, getDecimalAmountNumber } from 'utils/formatBalance' + +// 判断余额 +export const useCheckTokenBalance = () => { + const { balance: usdtTokenBalance } = useTokenBalance(getAddress(tokensList.usdt.address)) + const { toastWarning } = useToast() + const { t } = useTranslation() + const onCheck = useCallback( + (usdtAmount) => { + if (getBalanceNumber(usdtTokenBalance) <= usdtAmount) { + toastWarning(t('Insufficient Balance')) + return false + } + return true + }, + [usdtTokenBalance], + ) + return [onCheck] +} + +// 授权usdt +export const useApproveIdo = (tokenContract: Contract) => { + const handleApprove = useCallback(async () => { + try { + const tx = await tokenContract.approve(getIdoPurchaseAddress(), ethers.constants.MaxUint256) + const receipt = await tx.wait() + return receipt.status + } catch (e) { + return false + } + }, [tokenContract]) + + return { onApprove: handleApprove } +} + +// 兑换 +export const useBuyTransaction = () => { + const idoPurchase = useIdoPurchase() + const transaction = async (amount) => { + const data = await getPurchaseActivity() + const { id } = data + const params = [id, getDecimalAmount(amount).toString()] + const res = await idoPurchase.purchaseWithLock(...params) + return res + } + return transaction +} + +// 获取信息 +export const useGetRound = () => { + const transaction = async () => { + const data = await getPurchaseActivity() + if (!data) { + return null + } + const { id } = data + const idoPurchaseAddress = getIdoPurchaseAddress() + const calls = [ + { + address: idoPurchaseAddress, + name: 'getRound', + params: [id], + }, + ] + const res = await multicall(idoPurchaseABI, calls) + const detail = res.map((item) => { + return { + beginTime: new BigNumber(item?.beginTime._hex).toNumber() * 1000, + endTime: new BigNumber(item?.endTime._hex).toNumber() * 1000, + price: 1 / formatDivNumber(item?.price._hex, 18), + remaining: formatDivNumber(item?.remaining._hex, 18), + // remaining: new BigNumber(item?.remaining._hex).toString(), + token: item?.token, + // total: new BigNumber(item?.total._hex).toString(), + total: formatDivNumber(item?.total._hex, 18), + } + }) + return detail[0] + } + return transaction +} + +// 获取锁仓时间 +export const useLockTime = () => { + const transaction = async () => { + const idoPurchaseAddress = getIdoPurchaseAddress() + const calls = [ + { + address: idoPurchaseAddress, + name: 'lockTime', + }, + ] + const [res] = await multicall(idoPurchaseABI, calls) + return new BigNumber(res[0]._hex).toNumber() * 1000 + } + return transaction +} + +// 获取领取数量 +export const useUserInfo = () => { + const transaction = async (account) => { + const idoPurchaseAddress = getIdoPurchaseAddress() + const calls = [ + { + address: idoPurchaseAddress, + name: 'userInfo', + params: [account], + }, + ] + const [res] = await multicall(idoPurchaseABI, calls) + return formatDivNumber(res.purchasedAmount._hex, 18) + } + return transaction +} + +// 领取收益 +export const useHarvest = () => { + const idoPurchase = useIdoPurchase() + const transaction = async () => { + const res = await idoPurchase.harvest() + return res + } + return transaction +} + +export default useApproveIdo diff --git a/src/views/Ido/index.tsx b/src/views/Ido/index.tsx new file mode 100644 index 0000000..5b17e0a --- /dev/null +++ b/src/views/Ido/index.tsx @@ -0,0 +1,72 @@ +import React, { useState, useEffect } from 'react' +import styled from 'styled-components' +import { useAccount } from 'state/userInfo/hooks' +import useRefresh from 'hooks/useRefresh' +import { useLockTime, useUserInfo, useGetRound } from './hooks' +import IdoPurchaseCard from './components/IdoPurchaseCard' +import GetCard from './components/GetCard' + +interface RoundDetailProps { + beginTime?: number + endTime?: number + price?: number + remaining?: number + token?: string + total?: number +} + +const PageContent = styled.div` + min-height: calc(100vh - 64px); + background-image: url('/images/recommend/bg.svg'); + background-size: cover; + background-repeat: no-repeat; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +` + +const Exchange: React.FC = () => { + const account = useAccount() + + const lockTime = useLockTime() + const [time, setTime] = useState(0) + + const userInfo = useUserInfo() + const [price, setPrice] = useState(0) + + const [status, setStatus] = useState('none') // none proceed end + const [roundDetail, setRoundDetail] = useState({}) + const getRound = useGetRound() + const init = async () => { + const detail = await getRound() + if (!detail) { + setStatus('none') + } else if (detail.beginTime < new Date().getTime() && detail.endTime > new Date().getTime()) { + setStatus('proceed') + } else { + setStatus('end') + } + setRoundDetail(detail) + + const data = await lockTime() + setTime(data) + + const info = await userInfo(account) + setPrice(info) + } + const { fastRefresh } = useRefresh() + useEffect(() => { + if (account) { + init() + } + }, [account, fastRefresh]) + + return ( + + + {price > 0 && } + + ) +} +export default Exchange