├── .eslintrc.js ├── .eslintrc.json ├── .gitignore ├── .idea ├── .gitignore ├── dapp.iml ├── inspectionProfiles │ └── Project_Default.xml ├── jsLibraryMappings.xml ├── material_theme_project_new.xml ├── modules.xml ├── prettier.xml └── vcs.xml ├── .prettierrc ├── README.md ├── next.config.mjs ├── package.json ├── postcss.config.js ├── public ├── BG 960 x 857_1.webm ├── desktop-banner.webm ├── favicon.ico ├── favicon.png ├── logo.png ├── mobile BG 960 x 857_1.webm ├── mobile.webm ├── new BG 960 x 857.webm ├── next.svg ├── thumbnail.png └── vercel.svg ├── src ├── common │ ├── assets │ │ ├── animations │ │ │ └── UNICE-loading.json │ │ ├── images │ │ │ ├── NoProjectIcon.png │ │ │ ├── NoProjectIconDark.png │ │ │ ├── coin-star.png │ │ │ ├── cornerBottomLeft.png │ │ │ ├── cornerBottomRight.png │ │ │ ├── cornerTopLeft.png │ │ │ ├── cornerTopRight.png │ │ │ ├── frens.png │ │ │ ├── logo-fewcha.png │ │ │ ├── logo-mb.png │ │ │ ├── logo.png │ │ │ ├── lottery.png │ │ │ ├── my wallet bg.png │ │ │ ├── staking │ │ │ │ ├── APT.png │ │ │ │ ├── Line 32.png │ │ │ │ ├── Line 33.png │ │ │ │ ├── Line 38.png │ │ │ │ ├── Trash.png │ │ │ │ ├── VIP 1.png │ │ │ │ ├── VIP 2.png │ │ │ │ ├── VIP 3.png │ │ │ │ ├── VIP 4.png │ │ │ │ ├── VIP 5.png │ │ │ │ ├── VIP 6.png │ │ │ │ ├── VIP 7.png │ │ │ │ ├── VIP 8.png │ │ │ │ ├── bg-staking-1.png │ │ │ │ ├── bg-staking-item-1.png │ │ │ │ ├── bg-staking-item-2.png │ │ │ │ ├── bg-staking-item-3.png │ │ │ │ ├── defaultNFTImageList.png │ │ │ │ ├── defaultNFTImg.png │ │ │ │ ├── emptyNFTDetails.png │ │ │ │ ├── emptyNFTImg.png │ │ │ │ ├── mobile_VIP 1.png │ │ │ │ ├── mobile_VIP 2.png │ │ │ │ ├── mobile_VIP 3.png │ │ │ │ ├── mobile_VIP 4.png │ │ │ │ ├── mobile_VIP 5.png │ │ │ │ ├── mobile_VIP 6.png │ │ │ │ ├── mobile_VIP 7.png │ │ │ │ ├── mobile_VIP 8.png │ │ │ │ ├── nft.png │ │ │ │ ├── nftImage.png │ │ │ │ ├── selected-pool-icon.png │ │ │ │ └── unice-logo 1.png │ │ │ └── unice-logo-icon.png │ │ └── svg │ │ │ ├── discord.svg │ │ │ ├── discordFooter.svg │ │ │ ├── telegram.svg │ │ │ ├── telegramFooter.svg │ │ │ ├── twitter.svg │ │ │ ├── twitterFooter.svg │ │ │ └── website.svg │ ├── components │ │ ├── Coins │ │ │ └── CoinIcon │ │ │ │ └── index.tsx │ │ ├── Footer │ │ │ └── index.tsx │ │ ├── Header │ │ │ ├── index.tsx │ │ │ └── routers.ts │ │ ├── Modal │ │ │ ├── ModalConnectWallet.tsx │ │ │ ├── ModalNotification.tsx │ │ │ └── ModalTokens.tsx │ │ ├── PositiveFloatNumInput │ │ │ ├── index.tsx │ │ │ └── numberFormats.ts │ │ ├── Skeleton │ │ │ └── index.tsx │ │ ├── Views │ │ │ └── Staking │ │ │ │ ├── CardStaking.tsx │ │ │ │ ├── ModalReceiveBnb.tsx │ │ │ │ ├── ModalStaking.tsx │ │ │ │ ├── ModalStakingPools.tsx │ │ │ │ ├── ModalUnStaking.tsx │ │ │ │ ├── Referral │ │ │ │ ├── ModalTopReferrers.tsx │ │ │ │ └── index.tsx │ │ │ │ ├── StakingCommonComponent │ │ │ │ ├── StatusColumn.tsx │ │ │ │ └── VotingReward.tsx │ │ │ │ ├── StakingDetails.tsx │ │ │ │ ├── StakingPoolTab │ │ │ │ ├── StakingPoolAPR.tsx │ │ │ │ ├── StakingPoolDuration.tsx │ │ │ │ ├── StakingPoolItem.tsx │ │ │ │ └── StakingPoolTabContent.tsx │ │ │ │ ├── StakingReward.tsx │ │ │ │ ├── StakingTab │ │ │ │ └── index.tsx │ │ │ │ ├── UnStakingTab │ │ │ │ └── index.tsx │ │ │ │ └── VipLevelDetails.tsx │ │ ├── common-components │ │ │ ├── CustomProgress │ │ │ │ └── index.tsx │ │ │ ├── DisableInput │ │ │ │ └── index.tsx │ │ │ ├── InfiniteScroll │ │ │ │ └── index.tsx │ │ │ ├── InputCureency │ │ │ │ └── index.tsx │ │ │ └── MobileMenu │ │ │ │ ├── BottomMenu.module.css │ │ │ │ └── index.tsx │ │ ├── countdowns │ │ │ ├── CampaignTitle.tsx │ │ │ ├── CountdownActiveCampaign.tsx │ │ │ ├── CountdownLBP.tsx │ │ │ ├── CountdownUpcoming.tsx │ │ │ ├── CountdownUpcoming2.tsx │ │ │ └── SaleSchedule.tsx │ │ └── icon │ │ │ └── common │ │ │ └── index.tsx │ ├── configs │ │ ├── aptosContants.ts │ │ └── config.ts │ ├── consts │ │ └── index.ts │ ├── contracts │ │ ├── abis │ │ │ ├── ERC20.json │ │ │ ├── ERC721.json │ │ │ ├── contract.json │ │ │ ├── contractFrens.json │ │ │ ├── index.js │ │ │ ├── launchpad15Abi.json │ │ │ ├── market.json │ │ │ ├── token.json │ │ │ ├── vesting-claim.json │ │ │ └── vesting.json │ │ └── contract.ts │ ├── graphql │ │ └── index.ts │ ├── hooks │ │ ├── useLocalStoragre.ts │ │ ├── useLocalStoragre.tsx │ │ ├── useModal.tsx │ │ ├── useStaking.ts │ │ └── useUser.ts │ ├── layout │ │ └── LayoutPage.tsx │ ├── models │ │ └── common.d.ts │ ├── providers │ │ ├── AppProvider.tsx │ │ └── contexts │ │ │ └── index.tsx │ ├── services │ │ ├── aptosQueryClient.ts │ │ ├── instance.ts │ │ ├── login │ │ │ └── index.tsx │ │ ├── queryClient.ts │ │ ├── staking │ │ │ └── index.tsx │ │ └── upload.ts │ ├── stores │ │ ├── actions │ │ │ └── appAction.ts │ │ ├── reducers │ │ │ ├── appReducer.ts │ │ │ └── index.ts │ │ └── store.ts │ └── types │ │ ├── common.d.ts │ │ └── comon.ts ├── pages │ ├── _app.tsx │ ├── _document.tsx │ ├── api │ │ └── hello.ts │ ├── index.tsx │ ├── leaderboard │ │ └── index.tsx │ └── staking │ │ └── index.tsx ├── styles │ ├── antd.css │ ├── globals.css │ ├── notification.css │ └── notifications.css └── utils │ ├── address.ts │ ├── date.ts │ ├── index.tsx │ ├── launchpad.ts │ └── network.ts ├── tailwind.config.ts ├── tsconfig.json ├── unice.zip └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true, 4 | browser: true, 5 | es2021: true, 6 | }, 7 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react/recommended'], 8 | overrides: [ 9 | { 10 | env: { 11 | node: true, 12 | }, 13 | files: ['.eslintrc.{js,cjs}'], 14 | parserOptions: { 15 | sourceType: 'script', 16 | }, 17 | }, 18 | ], 19 | parser: '@typescript-eslint/parser', 20 | parserOptions: { 21 | ecmaVersion: 'latest', 22 | sourceType: 'module', 23 | }, 24 | plugins: ['@typescript-eslint', 'react'], 25 | rules: { 26 | // suppress errors for missing 'import React' in files 27 | 'react/react-in-jsx-scope': 'off', 28 | // allow jsx syntax in js files (for next.js project) 29 | 'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx', 'ts', 'tsx'] }], 30 | '@typescript-eslint/no-explicit-any': ['off'], 31 | '@typescript-eslint/no-var-requires': 0, 32 | 'no-unused-vars': 'off', 33 | '@typescript-eslint/no-unused-vars': 'off', 34 | '@typescript-eslint/no-non-null-assertion': 'off', 35 | 'react/prop-types': 'off', 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env 30 | .env*.local 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | 39 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # GitHub Copilot persisted chat sessions 7 | /copilot/chatSessions 8 | -------------------------------------------------------------------------------- /.idea/dapp.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 44 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/material_theme_project_new.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/prettier.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-organize-imports"], 3 | "singleQuote": true, 4 | "bracketSpacing": true, 5 | "printWidth": 120, 6 | "organizeImportsSkipDestructiveCodeActions": true 7 | } 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `pages/ProjectInfo.tsx`. The page auto-updates as you edit the file. 20 | 21 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. 22 | 23 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 24 | 25 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 26 | 27 | ## Learn More 28 | 29 | To learn more about Next.js, take a look at the following resources: 30 | 31 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 32 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 33 | 34 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 35 | 36 | ## Deploy on Vercel 37 | 38 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 39 | 40 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 41 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | output: 'export', 4 | reactStrictMode: true, 5 | env: { 6 | CURRENT_NETWORK: process.env.CURRENT_NETWORK, 7 | API_ENDPOINT_URL: process.env.API_ENDPOINT_URL, 8 | }, 9 | transpilePackages: [ 10 | 'antd', 11 | '@ant-design', 12 | 'rc-util', 13 | 'rc-pagination', 14 | 'rc-picker', 15 | 'rc-notification', 16 | 'rc-tooltip', 17 | 'rc-tree', 18 | 'rc-table', 19 | ], 20 | images: { 21 | unoptimized: true, 22 | domains: [ 23 | 'storage.googleapis.com', 24 | 'nft-metadata.testnet.aptoslabs.com', 25 | 'www.google.com', 26 | 'huggingface.co', 27 | 'letsenhance.io', 28 | 'img-cdn.pixlr.com', 29 | ], 30 | minimumCacheTTL: 1500000, 31 | }, 32 | }; 33 | 34 | export default nextConfig; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dapp", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev -p 3030", 7 | "build": "next build", 8 | "format": "npx prettier --write .", 9 | "start": "next start", 10 | "lint": "next lint" 11 | }, 12 | "dependencies": { 13 | "@rainbow-me/rainbowkit": "^2.2.0", 14 | "@reduxjs/toolkit": "^1.9.5", 15 | "@tailwindcss/aspect-ratio": "^0.4.2", 16 | "@tailwindcss/line-clamp": "^0.4.4", 17 | "@tanstack/react-query": "4.29.19", 18 | "@wagmi/connectors": "^5.5.0", 19 | "@wagmi/core": "^2.14.6", 20 | "antd": "^4.21.7", 21 | "axios": "^1.7.7", 22 | "bignumber.js": "^9.1.2", 23 | "draft-js": "^0.11.7", 24 | "draftjs-to-html": "^0.9.1", 25 | "ga-gtag": "^1.2.0", 26 | "jwt-decode": "^4.0.0", 27 | "moment": "^2.30.1", 28 | "next": "14.1.3", 29 | "next-seo": "^6.5.0", 30 | "ramda": "^0.29.1", 31 | "react": "^18", 32 | "react-device-detect": "^2.2.3", 33 | "react-dom": "^18", 34 | "react-draft-wysiwyg": "^1.15.0", 35 | "react-infinite-scroll-component": "^6.1.0", 36 | "react-loading-skeleton": "^3.4.0", 37 | "react-markdown": "^9.0.1", 38 | "react-number-format": "^5.3.3", 39 | "react-redux": "^8.1.1", 40 | "react-slick": "^0.30.2", 41 | "react-swipeable": "^7.0.1", 42 | "sass": "^1.77.8", 43 | "slick-carousel": "^1.8.1", 44 | "tiny-invariant": "^1.3.1", 45 | "viem": "2.x", 46 | "wagmi": "^2.13.0", 47 | "web3": "^1.10.2" 48 | }, 49 | "devDependencies": { 50 | "@babel/runtime": "^7.24.1", 51 | "@types/node": "^20", 52 | "@types/react": "^18", 53 | "@types/react-dom": "^18", 54 | "@types/react-slick": "^0.23.13", 55 | "@typescript-eslint/eslint-plugin": "^6.0.0", 56 | "@typescript-eslint/parser": "^7.2.0", 57 | "autoprefixer": "^10.0.1", 58 | "eslint": "^8", 59 | "eslint-config-next": "14.1.3", 60 | "postcss": "^8", 61 | "prettier": "^3.2.5", 62 | "prettier-plugin-organize-imports": "^3.2.4", 63 | "tailwindcss": "^3.3.0", 64 | "typescript": "^5" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/BG 960 x 857_1.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/public/BG 960 x 857_1.webm -------------------------------------------------------------------------------- /public/desktop-banner.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/public/desktop-banner.webm -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/public/favicon.png -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/public/logo.png -------------------------------------------------------------------------------- /public/mobile BG 960 x 857_1.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/public/mobile BG 960 x 857_1.webm -------------------------------------------------------------------------------- /public/mobile.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/public/mobile.webm -------------------------------------------------------------------------------- /public/new BG 960 x 857.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/public/new BG 960 x 857.webm -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/public/thumbnail.png -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/common/assets/animations/UNICE-loading.json: -------------------------------------------------------------------------------- 1 | {"v":"5.9.0","fr":29.6943817138672,"ip":0,"op":31.0003985023871,"w":200,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100.5,101,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[160,160],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.290196078431,0.490196078431,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.5,-1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"t":28.00035993764,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[0]},{"t":31.0003985023871,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":31.0003985023871,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100.5,101,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[160,160],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.066666666667,0.070588235294,0.082352941176,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.5,-1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":31.0003985023871,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /src/common/assets/images/NoProjectIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/NoProjectIcon.png -------------------------------------------------------------------------------- /src/common/assets/images/NoProjectIconDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/NoProjectIconDark.png -------------------------------------------------------------------------------- /src/common/assets/images/coin-star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/coin-star.png -------------------------------------------------------------------------------- /src/common/assets/images/cornerBottomLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/cornerBottomLeft.png -------------------------------------------------------------------------------- /src/common/assets/images/cornerBottomRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/cornerBottomRight.png -------------------------------------------------------------------------------- /src/common/assets/images/cornerTopLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/cornerTopLeft.png -------------------------------------------------------------------------------- /src/common/assets/images/cornerTopRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/cornerTopRight.png -------------------------------------------------------------------------------- /src/common/assets/images/frens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/frens.png -------------------------------------------------------------------------------- /src/common/assets/images/logo-fewcha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/logo-fewcha.png -------------------------------------------------------------------------------- /src/common/assets/images/logo-mb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/logo-mb.png -------------------------------------------------------------------------------- /src/common/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/logo.png -------------------------------------------------------------------------------- /src/common/assets/images/lottery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/lottery.png -------------------------------------------------------------------------------- /src/common/assets/images/my wallet bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/my wallet bg.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/APT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/APT.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/Line 32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/Line 32.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/Line 33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/Line 33.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/Line 38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/Line 38.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/Trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/Trash.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/VIP 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/VIP 1.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/VIP 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/VIP 2.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/VIP 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/VIP 3.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/VIP 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/VIP 4.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/VIP 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/VIP 5.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/VIP 6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/VIP 6.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/VIP 7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/VIP 7.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/VIP 8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/VIP 8.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/bg-staking-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/bg-staking-1.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/bg-staking-item-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/bg-staking-item-1.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/bg-staking-item-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/bg-staking-item-2.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/bg-staking-item-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/bg-staking-item-3.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/defaultNFTImageList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/defaultNFTImageList.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/defaultNFTImg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/defaultNFTImg.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/emptyNFTDetails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/emptyNFTDetails.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/emptyNFTImg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/emptyNFTImg.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/mobile_VIP 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/mobile_VIP 1.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/mobile_VIP 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/mobile_VIP 2.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/mobile_VIP 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/mobile_VIP 3.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/mobile_VIP 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/mobile_VIP 4.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/mobile_VIP 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/mobile_VIP 5.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/mobile_VIP 6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/mobile_VIP 6.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/mobile_VIP 7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/mobile_VIP 7.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/mobile_VIP 8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/mobile_VIP 8.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/nft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/nft.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/nftImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/nftImage.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/selected-pool-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/selected-pool-icon.png -------------------------------------------------------------------------------- /src/common/assets/images/staking/unice-logo 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/staking/unice-logo 1.png -------------------------------------------------------------------------------- /src/common/assets/images/unice-logo-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdev071/unice-staking/99de3591e9dc57473f2296505afe98bffb2f296f/src/common/assets/images/unice-logo-icon.png -------------------------------------------------------------------------------- /src/common/assets/svg/discord.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/common/assets/svg/discordFooter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/common/assets/svg/telegram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/common/assets/svg/telegramFooter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/common/assets/svg/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/common/assets/svg/twitterFooter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/common/assets/svg/website.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/common/components/Coins/CoinIcon/index.tsx: -------------------------------------------------------------------------------- 1 | import { Tooltip } from 'antd'; 2 | import classNames from 'classnames'; 3 | import React, { useCallback, useState } from 'react'; 4 | import Skeleton from '../../Skeleton'; 5 | 6 | interface TProps { 7 | logoSrc?: string; 8 | className?: string; 9 | token?: any; 10 | size?: number; 11 | isShowSymbol?: boolean; 12 | isFetching?: boolean; 13 | } 14 | 15 | // Use size instead of className to set the size of images 16 | const CoinIcon: React.FC = ({ logoSrc, size = 24, className, token, isShowSymbol = false, isFetching }) => { 17 | const [isLoaded, setIsLoaded] = useState(false); 18 | const onImgError = () => { 19 | setIsLoaded(false); 20 | }; 21 | const onImgLoad = useCallback(() => { 22 | setIsLoaded(true); 23 | }, []); 24 | 25 | const bridgeArray = token?.extensions.data.find((a: any) => a[0] === 'bridge'); 26 | const bridge = (bridgeArray && bridgeArray[1]) ?? ''; 27 | 28 | return ( 29 |
30 | {(!logoSrc || isFetching) && ( 31 | 32 | )} 33 | 37 | {logoSrc && ( 38 | coin icon 45 | )} 46 | 47 |
48 | ); 49 | }; 50 | 51 | export default CoinIcon; 52 | -------------------------------------------------------------------------------- /src/common/components/Footer/index.tsx: -------------------------------------------------------------------------------- 1 | import { THEME } from '@/common/components/Header'; 2 | import { DiscordIcon, DocsIcon1, TelegramIcon, TwitterIcon } from '@/common/components/icon/common'; 3 | import { copyToClipboard } from '@/utils'; 4 | import { Tooltip } from 'antd'; 5 | import Image from 'next/image'; 6 | import Link from 'next/link'; 7 | import React, { useEffect, useState } from 'react'; 8 | import { useSelector } from 'react-redux'; 9 | 10 | const Footer: React.FunctionComponent = () => { 11 | const [copyText, setCopyText] = useState('Copy'); 12 | const [currentTheme, setCurrentTheme] = useState(THEME.DARK); 13 | const app = useSelector((state: any) => state.app); 14 | 15 | useEffect(() => { 16 | setCurrentTheme(app?.theme == 'light' ? THEME.DARK : THEME.DARK); 17 | }, [app]); 18 | 19 | const handleCopy = (value: string) => { 20 | setCopyText('Copied!'); 21 | setTimeout(() => { 22 | setCopyText('Copy'); 23 | }, 2000); 24 | copyToClipboard(value); 25 | }; 26 | 27 | return ( 28 |
33 |
38 |
© 2024 Unice Lab Pte., Ltd.
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
53 |
54 |
55 | ); 56 | }; 57 | export default Footer; 58 | -------------------------------------------------------------------------------- /src/common/components/Header/routers.ts: -------------------------------------------------------------------------------- 1 | export const routes = [ 2 | { 3 | path: 'staking', 4 | target: false, 5 | name: 'STAKING', 6 | hot: false, 7 | comingSoon: false, 8 | hidden: false, 9 | }, 10 | { 11 | path: 'https://wallet.unicelab.io/', 12 | target: true, 13 | name: 'WALLET', 14 | hot: false, 15 | comingSoon: false, 16 | hidden: false, 17 | }, 18 | { 19 | path: 'https://docs.unicelab.io/', 20 | target: true, 21 | name: 'DOCS', 22 | hot: false, 23 | comingSoon: false, 24 | hidden: false, 25 | }, 26 | // { 27 | // path: 'vesting', 28 | // target: false, 29 | // name: 'Claim token', 30 | // hot: true, 31 | // comingSoon: false, 32 | // hidden: false, 33 | // }, 34 | ]; 35 | -------------------------------------------------------------------------------- /src/common/components/Modal/ModalConnectWallet.tsx: -------------------------------------------------------------------------------- 1 | import fewcha from '@/common/assets/images/logo-fewcha.png'; 2 | 3 | import { CloseIcon, DotIcon } from '@/common/components/icon/common'; 4 | import { config } from '@/common/consts'; 5 | import { showConnect } from '@/common/stores/actions/appAction'; 6 | import { Modal } from 'antd'; 7 | import React, { useContext, useEffect, useState } from 'react'; 8 | import { isIOS, isMobile } from 'react-device-detect'; 9 | import { useDispatch } from 'react-redux'; 10 | import { Connector, useConnect } from 'wagmi'; 11 | 12 | interface Props { 13 | isModalOpen: boolean; 14 | handleClose: () => void; 15 | } 16 | 17 | enum WalletName { 18 | Petra = 'Petra', 19 | Pontem = 'Pontem', 20 | Martian = 'Martian', 21 | Fewcha = 'Fewcha', 22 | RiseWallet = 'Rise Wallet', 23 | OKXWallet = 'OKX Wallet', 24 | } 25 | 26 | const ModalConnectWallet: React.FunctionComponent = ({ isModalOpen, handleClose }) => { 27 | const { connectors, connect } = useConnect(); 28 | const [connectSuccess, setConnectSuccess] = useState(false); 29 | const dispatch = useDispatch(); 30 | 31 | const [isOkApp, setIsOkApp] = useState(false); 32 | const [encodedUrl, setEncodedUrl] = useState(''); 33 | 34 | useEffect(() => { 35 | const ua = navigator.userAgent; 36 | const check = /OKApp/i.test(ua); 37 | setIsOkApp(check); 38 | const url = 39 | 'https://www.okx.com/download?deeplink=' + 40 | encodeURIComponent('okx://wallet/dapp/url?dappUrl=' + encodeURIComponent(location.href)); 41 | setEncodedUrl(url); 42 | }, []); 43 | 44 | // useEffect(() => { 45 | // (async () => { 46 | // if (connectSuccess) { 47 | // await handleSignMessage(); 48 | // } 49 | // })(); 50 | // }, [connectSuccess]); 51 | 52 | // const handleSignMessage = async () => { 53 | // try { 54 | // // const {data} = await getMessage({publicKey: account?.publicKey as string}); 55 | // const nonce = await getNonce(account?.address as string); 56 | // const res: any = (await signMessage({ 57 | // address: false, 58 | // message: `Click to sign in and accept the MOVEGPT Terms of Service. This request will not cost any gas fees.`, 59 | // nonce: nonce.nonce, 60 | // })) as SignMessageResponse; 61 | // if (res) { 62 | // const resLogin = await loginUser({ 63 | // addr: account?.address as string, 64 | // massage: res.fullMessage, 65 | // signature: res.signature.data 66 | // ? new Buffer(res.signature).toString('hex') 67 | // : typeof res.signature === 'string' 68 | // ? res.signature 69 | // : HexString.fromUint8Array(res.signature as any).toString(), 70 | // publicKey: account?.publicKey as string, 71 | // }); 72 | // setData('accessToken', resLogin.data.token); 73 | // await refetchUser(); 74 | // setConnectSuccess(false); 75 | // } else { 76 | // setConnectSuccess(false); 77 | // await removeData('accessToken'); 78 | // await disconnect(); 79 | // } 80 | // } catch (e) { 81 | // console.log('e', e); 82 | // setConnectSuccess(false); 83 | // await removeData('accessToken'); 84 | // await disconnect(); 85 | // } 86 | // }; 87 | 88 | return ( 89 | 99 |
100 |
101 |
Connect wallet
102 |
103 | 104 |
105 |
106 |
Choose a wallet you want connect to
107 |
108 |
    109 | {connectors.map((connector: Connector) => { 110 | return ( 111 |
  • { 113 | connect({ connector }); 114 | handleClose(); 115 | }} 116 | className={'wallet-item cursor-pointer p-4 rounded-[6px]'} 117 | key={connector.uid} 118 | > 119 |
    120 |
    121 |
    122 | 123 |
    124 |
    127 | {connector.name} 128 |
    129 |
    130 |
    131 |
    132 |
    133 |
  • 134 | ); 135 | })} 136 |
137 |
138 |
139 |
140 | ); 141 | }; 142 | export default ModalConnectWallet; 143 | -------------------------------------------------------------------------------- /src/common/components/Modal/ModalNotification.tsx: -------------------------------------------------------------------------------- 1 | import animationData from '@/common/assets/animations/UNICE-loading.json'; 2 | import { FailedIcon, PendingIcon, StakingWarningIcon, SuccessIcon } from '@/common/components/icon/common'; 3 | import { ellipseAddress, formatNumber } from '@/utils'; 4 | import { Button, Modal } from 'antd'; 5 | import Link from 'next/link'; 6 | import React, { ReactNode } from 'react'; 7 | // import Lottie from 'react-lottie'; 8 | 9 | interface Props { 10 | type: string; 11 | iconTitle?: ReactNode; 12 | title?: string; 13 | desc: any; 14 | unstakeInfo?: any; 15 | isModalOpen: boolean; 16 | width?: number; 17 | handleClose?: () => void; 18 | setIsConfirmUnstake?: (val: boolean) => void; 19 | buttonTitle?: string; 20 | confirmAction?: () => void; 21 | confirmCallback?: () => void; 22 | } 23 | 24 | export enum TYPE { 25 | PENDING, 26 | SUCCESS, 27 | FAILED, 28 | } 29 | 30 | const ModalNotification: React.FunctionComponent = ({ 31 | type, 32 | iconTitle, 33 | title, 34 | desc, 35 | unstakeInfo, 36 | isModalOpen, 37 | width, 38 | handleClose, 39 | setIsConfirmUnstake, 40 | buttonTitle, 41 | confirmAction, 42 | confirmCallback, 43 | }) => { 44 | // const defaultOptions = { 45 | // loop: true, 46 | // autoplay: true, 47 | // animationData: animationData, 48 | // renderer: 'svg', 49 | // }; 50 | return ( 51 | 61 |
62 | {type === 'PENDING' && } 63 | {type === 'SUCCESS' && } 64 | {type === 'FAILED' && } 65 | {type === 'WARNING' && } 66 | {type === 'CUSTOM' && iconTitle} 67 |
68 | {title &&
{title}
} 69 |
{desc}
70 |
71 | {unstakeInfo && ( 72 |
73 |
74 |
Unstaked
75 |
{unstakeInfo?.amount} UNICE
76 |
77 |
78 |
Claimed reward
79 |
{formatNumber(unstakeInfo?.reward)} UNICE
80 |
81 |
82 |
Transaction hash
83 | 84 |
{ellipseAddress(unstakeInfo?.hash, 5)}
85 | 86 |
87 |
88 | )} 89 | {type !== 'PENDING' && type !== 'WARNING' && ( 90 | 97 | )} 98 | {type === 'WARNING' && ( 99 |
100 | 113 | 120 |
121 | )} 122 |
123 |
124 | ); 125 | }; 126 | 127 | export default ModalNotification; 128 | -------------------------------------------------------------------------------- /src/common/components/PositiveFloatNumInput/index.tsx: -------------------------------------------------------------------------------- 1 | import { forwardRef, useImperativeHandle, useRef } from 'react'; 2 | import { NumericFormat } from 'react-number-format'; 3 | 4 | type PositiveFloatNumInputProps = { 5 | inputAmount?: string; 6 | className?: string; 7 | isDisabled?: boolean; 8 | isAllowDecimal?: boolean; 9 | placeholder?: string; 10 | min?: number; 11 | max?: number; 12 | maxDecimals?: number; 13 | onInputChange?: (e: any) => void; 14 | onAmountChange?: (a: number) => void; 15 | prefix?: string; 16 | suffix?: string; 17 | label?: string; 18 | showCommas?: boolean; 19 | }; 20 | 21 | // const MIN_DEFAULT = 0.00000001; 22 | const MAX_DEFAULT = Number.MAX_SAFE_INTEGER; 23 | const MAX_DECIMALS_DEFAULT = 9; 24 | 25 | // eslint-disable-next-line react/display-name 26 | const PositiveFloatNumInput = forwardRef( 27 | ( 28 | { 29 | inputAmount, 30 | isDisabled = false, 31 | isAllowDecimal, 32 | label = '', 33 | // min = MIN_DEFAULT, 34 | max = MAX_DEFAULT, 35 | maxDecimals = isAllowDecimal ? MAX_DECIMALS_DEFAULT : 0, 36 | onInputChange = () => {}, 37 | className, 38 | placeholder = '0.0000', 39 | }, 40 | ref, 41 | ) => { 42 | const inputRef = useRef(null); 43 | useImperativeHandle(ref, () => inputRef.current!); 44 | // const format = (numStr: any) => { 45 | // if (numStr === '') return ''; 46 | // return new Intl.NumberFormat('en-US', { 47 | // maximumFractionDigits: 0, 48 | // }).format(numStr); 49 | // }; 50 | return ( 51 | { 63 | const { formattedValue, floatValue } = values; 64 | return formattedValue === '' || Number(floatValue) <= max; 65 | }} 66 | onValueChange={(values: any) => { 67 | if (values.floatValue > max) { 68 | onInputChange(max); 69 | } else { 70 | onInputChange(values.floatValue); 71 | } 72 | }} 73 | disabled={isDisabled} 74 | /> 75 | ); 76 | }, 77 | ); 78 | 79 | export default PositiveFloatNumInput; 80 | -------------------------------------------------------------------------------- /src/common/components/PositiveFloatNumInput/numberFormats.ts: -------------------------------------------------------------------------------- 1 | import invariant from 'tiny-invariant'; 2 | 3 | export const numToGrouped = (num: string) => { 4 | return num 5 | .split('.') 6 | .map((v, index) => { 7 | if (index > 0) return v; 8 | else { 9 | return v 10 | .split('') 11 | .map((l, index2) => { 12 | const revertIndex = v.length - index2; 13 | if ((revertIndex - 1) % 3 === 0 && revertIndex !== 1) { 14 | return `${l},`; 15 | } else { 16 | return l; 17 | } 18 | }) 19 | .join(''); 20 | } 21 | }) 22 | .join('.'); 23 | }; 24 | 25 | export const avoidScientificNotation = (x: number) => { 26 | invariant(Math.abs(x) < Number.MAX_SAFE_INTEGER, 'Invalid number range of ' + x); 27 | let res = x.toString(); 28 | if (Math.abs(x) < 1.0) { 29 | const e = parseInt(x.toString().split('e-')[1]); 30 | if (e) { 31 | const y = Math.abs(x) * Math.pow(10, e - 1); 32 | res = (x > 0 ? '0.' : '-0.') + new Array(e).join('0') + y.toString().substring(2); 33 | } 34 | } 35 | // Note we don't consider the case x >= 1e21 which would also be converted to scientific notation by js 36 | return res; 37 | }; 38 | 39 | // Cut decimals to its max allowed count 40 | export const cutDecimals = (v: string, maxDecimals: number | undefined) => { 41 | const decimalsLength = v.split('.')[1]?.length || 0; 42 | if (typeof maxDecimals === 'number' && decimalsLength > maxDecimals) { 43 | v = v 44 | .split('.') 45 | .map((vs, index) => { 46 | if (index > 0) { 47 | return vs.slice(0, maxDecimals); 48 | } 49 | return vs; 50 | }) 51 | .join('.'); 52 | if (/^[\d]+\.$/.test(v)) v = v.replace('.', ''); 53 | } 54 | return v; 55 | }; 56 | 57 | export const numberGroupFormat = (amount: number, decimals = 2) => { 58 | return numToGrouped(cutDecimals(avoidScientificNotation(amount), decimals)); 59 | }; 60 | 61 | export const numberOfAbbr = (amount: number, decimals = 0) => { 62 | const grades = [ 63 | { dec: 9, abbr: 'B' }, 64 | { dec: 6, abbr: 'M' }, 65 | { dec: 3, abbr: 'K' }, 66 | ].sort((a, b) => b.dec - a.dec); 67 | for (const g of grades) { 68 | if (amount > 10 ** g.dec) { 69 | return cutDecimals(avoidScientificNotation(amount / 10 ** g.dec), decimals) + g.abbr; 70 | } 71 | } 72 | return cutDecimals(avoidScientificNotation(amount), decimals); 73 | }; 74 | 75 | export const percent = (v: number | string, maxDecimals: number | undefined = 2, hasSign = false): string => { 76 | if (typeof v === 'string') { 77 | const n = parseFloat(v); 78 | if (isNaN(n)) { 79 | return v; 80 | } 81 | v = n; 82 | } 83 | const limitValue = 1 / 10 ** (maxDecimals + 2); // Do use 1 / 10^n rather than 1 / 10^-n 84 | if (maxDecimals && Math.abs(v) > 0 && Math.abs(v) < limitValue) 85 | return `${v < 0 ? '> -' : '< '}${percent(limitValue, maxDecimals)}`; 86 | return (hasSign && v > 0 ? '+' : '') + cutDecimals(avoidScientificNotation(v * 100), maxDecimals) + '%'; 87 | }; 88 | 89 | export const numberGroupedOrExpontial = (num: number, decimals: number, threshold = 0.01) => { 90 | if (num < threshold) { 91 | return num.toExponential(decimals); 92 | } else { 93 | return numberGroupFormat(num, decimals); 94 | } 95 | }; 96 | -------------------------------------------------------------------------------- /src/common/components/Skeleton/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import LoadingSkeleton, { SkeletonProps as LoadingSkeletonProps } from 'react-loading-skeleton'; 3 | import 'react-loading-skeleton/dist/skeleton.css'; 4 | 5 | const Skeleton: React.FunctionComponent = ({ baseColor, highlightColor, ...rest }) => { 6 | const isDark = true; 7 | if (isDark) { 8 | baseColor = '#1C1D25'; 9 | highlightColor = '#2A2B39'; 10 | } 11 | return ; 12 | }; 13 | 14 | export default Skeleton; 15 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/ModalReceiveBnb.tsx: -------------------------------------------------------------------------------- 1 | import { STATUS } from '@/common/types/comon'; 2 | import React from 'react'; 3 | import ModalNotification from '../../Modal/ModalNotification'; 4 | 5 | interface Props { 6 | status: STATUS; 7 | show: boolean | undefined; 8 | toggle: () => void; 9 | setShow: (show: boolean) => void; 10 | message?: string; 11 | } 12 | 13 | const ModalReceiveBnb: React.FunctionComponent = ({ status, show, message, toggle, setShow }) => { 14 | if (status === STATUS.FAIL) { 15 | return ( 16 | setShow(false)} 22 | /> 23 | ); 24 | } 25 | if (status === STATUS.SUCCESS) { 26 | return ( 27 | { 34 | toggle(); 35 | window.location.reload(); 36 | }} 37 | /> 38 | ); 39 | } 40 | return ( 41 | 46 |
Processing...
47 |
Please wait ~5 minutes
48 | 49 | } 50 | isModalOpen={!!show} 51 | /> 52 | ); 53 | }; 54 | export default ModalReceiveBnb; 55 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/ModalStaking.tsx: -------------------------------------------------------------------------------- 1 | import ModalNotification from '@/common/components/Modal/ModalNotification'; 2 | import { STATUS } from '@/common/types/comon'; 3 | import React from 'react'; 4 | 5 | interface Props { 6 | amount: number; 7 | status: STATUS; 8 | show: boolean | undefined; 9 | txHash?: string; 10 | txVersion?: string; 11 | networkCfg?: any; 12 | error?: string; 13 | toggle: () => void; 14 | setShow: (show: boolean) => void; 15 | } 16 | 17 | const errorCase = { 18 | not_registered: "Campaign's user not found", 19 | }; 20 | 21 | enum ERROR_TEXT { 22 | NOT_REGISTERED = 'The wallet is not whitelisted!', 23 | } 24 | 25 | const ModalStaking: React.FunctionComponent = ({ amount, status, show, error, toggle, setShow }) => { 26 | if (status === STATUS.FAIL) { 27 | return ( 28 | setShow(false)} 34 | /> 35 | ); 36 | } 37 | if (status === STATUS.SUCCESS) { 38 | return ( 39 | 45 | You have successfully staked {amount} UNICE. 46 | 47 | } 48 | isModalOpen={!!show} 49 | handleClose={() => { 50 | toggle(); 51 | window.location.reload(); 52 | }} 53 | /> 54 | ); 55 | } 56 | return ( 57 | 64 | ); 65 | }; 66 | 67 | export default ModalStaking; 68 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/ModalUnStaking.tsx: -------------------------------------------------------------------------------- 1 | import ModalNotification from '@/common/components/Modal/ModalNotification'; 2 | import { STATUS } from '@/common/types/comon'; 3 | import React from 'react'; 4 | 5 | interface Props { 6 | isExpired: boolean; 7 | timeExpired: string; 8 | status: STATUS; 9 | show: boolean | undefined; 10 | unstakeInfo: any; 11 | txHash?: string; 12 | txVersion?: string; 13 | networkCfg?: any; 14 | error?: string; 15 | toggle: () => void; 16 | setShow: (show: boolean) => void; 17 | confirmUnstake: (pool: any) => void; 18 | } 19 | 20 | const errorCase = { 21 | not_registered: "Campaign's user not found", 22 | }; 23 | 24 | enum ERROR_TEXT { 25 | NOT_REGISTERED = 'The wallet is not whitelisted!', 26 | } 27 | 28 | const ModalUnStaking: React.FunctionComponent = ({ 29 | unstakeInfo, 30 | isExpired, 31 | timeExpired, 32 | status, 33 | show, 34 | error, 35 | toggle, 36 | setShow, 37 | confirmUnstake, 38 | }) => { 39 | if (status === STATUS.FAIL) { 40 | return ( 41 |
42 | {isExpired ? ( 43 | setShow(false)} 49 | /> 50 | ) : ( 51 | 56 | You can’t unstake until {timeExpired} 57 |
58 | } 59 | isModalOpen={!!show} 60 | handleClose={() => setShow(false)} 61 | buttonTitle={'Okay, I got it'} 62 | /> 63 | )} 64 | 65 | ); 66 | } 67 | if (status === STATUS.SUCCESS) { 68 | return ( 69 | { 77 | toggle(); 78 | window.location.reload(); 79 | }} 80 | /> 81 | ); 82 | } 83 | if (status === STATUS.WARNING) { 84 | return ( 85 | Do you want to unstake?} 90 | isModalOpen={!!show} 91 | handleClose={() => { 92 | toggle(); 93 | }} 94 | confirmCallback={() => confirmUnstake({})} 95 | /> 96 | ); 97 | } 98 | return ( 99 | 106 | ); 107 | }; 108 | 109 | export default ModalUnStaking; 110 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/Referral/ModalTopReferrers.tsx: -------------------------------------------------------------------------------- 1 | import { CloseIcon } from '@/common/components/icon/common'; 2 | import { Col, Modal, Pagination, PaginationProps, Row } from 'antd'; 3 | import React, { useState } from 'react'; 4 | 5 | interface Props { 6 | isModalOpen: boolean; 7 | handleClose: () => void; 8 | } 9 | 10 | const ModalTopReferrers: React.FunctionComponent = ({ isModalOpen, handleClose }) => { 11 | const [pageReferrer, setPageReferrer] = useState(1); 12 | const [currentPageReferrer, setCurrentPageReferrer] = useState(1); 13 | 14 | const onShowSizeChangeReferrer: PaginationProps['onShowSizeChange'] = (current, pageSize) => { 15 | setPageReferrer(current); 16 | setCurrentPageReferrer(current); 17 | }; 18 | 19 | return ( 20 | 30 |
31 |
32 |
Top referrers
33 |
34 | 35 |
36 |
37 | {/*
*/} 38 | {/*
*/} 39 | {/*
*/} 40 | {/* */} 41 | {/* */} 42 | {/* Rank*/} 43 | {/* */} 44 | {/* */} 45 | {/* Wallet*/} 46 | {/* */} 47 | {/* */} 48 | {/* Friends Staked*/} 49 | {/* */} 50 | {/* */} 51 | {/* {!isFetchingReferrer && leaderboardInfo.length > 0 && (*/} 52 | {/*
*/} 53 | {/* {leaderboardInfo?.map((item: any, index: number) => {*/} 54 | {/* return (*/} 55 | {/* */} 60 | {/* */} 61 | {/*
{item?.rank}
*/} 62 | {/* */} 63 | {/* */} 64 | {/*
{ellipseAddress(item?.address, 4)}
*/} 65 | {/*
{ellipseAddress(item?.address, 8)}
*/} 66 | {/* */} 67 | {/* */} 68 | {/*
{formatNumber(item?.totalAmount)}
*/} 69 | {/* */} 70 | {/* */} 71 | {/* );*/} 72 | {/* })}*/} 73 | {/*
*/} 74 | {/* )}*/} 75 | {/*
*/} 76 | {/*
*/} 77 | {/*
*/} 78 | {/*
*/} 79 | {/* Total {totalLeaderboardItem} participants*/} 80 | {/*
*/} 81 | {/* */} 90 | {/*
*/} 91 | {/*
*/} 92 | {/*
Your rank
*/} 93 | {/* */} 94 | {/* */} 95 | {/*
{userReferralInfo?.rank ? userReferralInfo?.rank : '--'}
*/} 96 | {/* */} 97 | {/* */} 98 | {/*
*/} 99 | {/* {connected ? ellipseAddress(account?.address as string, 5) : '--'}*/} 100 | {/*
*/} 101 | {/*
*/} 102 | {/* {connected ? ellipseAddress(account?.address as string, 8) : '--'}*/} 103 | {/*
*/} 104 | {/* */} 105 | {/* */} 106 | {/*
{formatNumber(userReferralInfo?.totalAmount)}
*/} 107 | {/* */} 108 | {/*
*/} 109 | {/*
*/} 110 | {/*
*/} 111 |
112 |
113 | ); 114 | }; 115 | 116 | export default ModalTopReferrers; 117 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/StakingCommonComponent/StatusColumn.tsx: -------------------------------------------------------------------------------- 1 | import { DotIcon } from '@/common/components/icon/common'; 2 | import { Button } from 'antd'; 3 | import React from 'react'; 4 | 5 | const StatusColumn: React.FunctionComponent<{ 6 | record: any; 7 | isMobile: boolean; 8 | setShowModalStaking: (value: any) => void; 9 | }> = ({ record, isMobile, setShowModalStaking }) => { 10 | return ( 11 |
12 |
13 |
14 |
15 |
16 | 17 |
Open
18 |
19 |
20 |
21 |
22 |
23 | 30 |
31 |
32 | ); 33 | }; 34 | 35 | export default StatusColumn; 36 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/StakingCommonComponent/VotingReward.tsx: -------------------------------------------------------------------------------- 1 | import { DEFAULT_DECIMALS } from '@/common/consts'; 2 | import { formatNumber } from '@/utils'; 3 | import BigNumber from 'bignumber.js'; 4 | import moment from 'moment/moment'; 5 | import React, { useMemo } from 'react'; 6 | 7 | const VotingRewardRerender: React.FunctionComponent<{ record: any }> = ({ record }) => { 8 | const unstakeTime = useMemo(() => { 9 | return moment(record?.close_at).add(Number(record?.est_apr[0].time), 'days').endOf('days').valueOf(); 10 | }, [record?.stakedInfo?.start_time]); 11 | 12 | const remainingTime = useMemo(() => { 13 | return (unstakeTime - moment().endOf('days').valueOf()) / (1000 * 60 * 60 * 24); 14 | }, [unstakeTime]); 15 | 16 | const votingPower = useMemo(() => { 17 | return BigNumber(record?.stakedInfo?.stake_amount) 18 | .minus(BigNumber(record?.stakedInfo?.claimed_amount)) 19 | .times(BigNumber(remainingTime)) 20 | .div(BigNumber(365 * 2)) 21 | .div(BigNumber(10).pow(DEFAULT_DECIMALS)) 22 | .toNumber(); 23 | }, [remainingTime]); 24 | 25 | return ( 26 |
27 | {votingPower && votingPower !== 0 ? formatNumber(votingPower) : 0} veMGPT 28 |
29 | ); 30 | }; 31 | 32 | export default VotingRewardRerender; 33 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/StakingDetails.tsx: -------------------------------------------------------------------------------- 1 | import Skeleton from '@/common/components/Skeleton'; 2 | import { formatNumber, formatRewardBalance } from '@/utils'; 3 | import { Col, Popover, Row } from 'antd'; 4 | import React, { useEffect, useMemo, useState } from 'react'; 5 | 6 | interface Props { 7 | loading: boolean; 8 | vipLevel: VipLevel; 9 | balanceStaked: number | string | any; 10 | stakeInfo: StakeInfo; 11 | poolInfo: PollInfo; 12 | } 13 | 14 | const StakingDetails: React.FunctionComponent = ({ loading, vipLevel, balanceStaked, stakeInfo, poolInfo }) => { 15 | return ( 16 | 17 | 18 | {loading ? ( 19 | 20 | ) : ( 21 |
22 |
Current VIP Tier
23 |
24 | {!vipLevel?.vip ? ( 25 |
26 | You are not yet a VIP. 27 |
Stake now to receive rewards and
participate in the IDO 28 |
29 | ) : ( 30 |
31 | {/**/} 32 | {vipLevel?.vip} 33 |
34 | )} 35 |
36 |
37 | )} 38 | 39 | 40 | {loading ? ( 41 | 42 | ) : ( 43 |
44 |
Staked Amount
45 |
46 | {/*{connected ? formatNumber(Number(stakeInfo?.stake_amount) / Math.pow(10, 8)) : 0} MGPT*/} 47 | {formatNumber(Number(vipLevel?.amount))} UNICE 48 |
49 |
50 | )} 51 | 52 | 53 | {loading ? ( 54 | 55 | ) : ( 56 |
57 |
58 |
Current voting power
59 | {/**/} 64 | {/* The previously accrued interest is retained and added to the final cumulative earnings at the end of*/} 65 | {/* the staking period.*/} 66 | {/*
*/} 67 | {/* }*/} 68 | {/* trigger="hover"*/} 69 | {/*>*/} 70 | {/*
*/} 71 | {/* */} 72 | {/*
*/} 73 | {/**/} 74 |
75 | {stakeInfo && balanceStaked != 0 ? ( 76 |
0 veMGPT
77 | ) : ( 78 |
-
79 | )} 80 | 81 | )} 82 | 83 | 84 | {loading ? ( 85 | 86 | ) : ( 87 |
88 |
{formatNumber(vipLevel?.required)} UNICE
89 |
90 | )} 91 | 92 |
93 | ); 94 | }; 95 | 96 | export default StakingDetails; 97 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/StakingPoolTab/StakingPoolAPR.tsx: -------------------------------------------------------------------------------- 1 | import { formatRewardBalance } from '@/utils'; 2 | import Image from 'next/image'; 3 | import React from 'react'; 4 | 5 | interface Props { 6 | uniceAPR: number; 7 | frensAPR: number; 8 | } 9 | 10 | const StakingPoolAPR: React.FunctionComponent = ({ uniceAPR, frensAPR }) => { 11 | return ( 12 |
13 |
14 |
15 | {''} 16 |
UNICE
17 |
18 |
{formatRewardBalance(uniceAPR, 2)}%
19 |
20 |
21 |
22 | {''} 23 |
FRENS
24 |
25 |
{formatRewardBalance(frensAPR, 2)}%
26 |
27 | {''} 28 |
29 |
30 |
Total APR
31 |
32 |
{formatRewardBalance(uniceAPR + frensAPR, 2)}%
33 |
34 |
35 | ); 36 | }; 37 | 38 | export default StakingPoolAPR; 39 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/StakingPoolTab/StakingPoolDuration.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import React, { useEffect, useMemo, useState } from 'react'; 3 | 4 | interface Props { 5 | poolIndex: number; 6 | totalPool: any; 7 | poolName: string; 8 | pools: any; 9 | pool: any; 10 | setPoolSelected: (value: any) => void; 11 | selectedItem: any; 12 | handleSelectItems: (poolName: any, selectedId: any) => void; 13 | handleSelectDuration: (poolId: number, duration: any) => void; 14 | } 15 | 16 | const StakingPoolDuration: React.FunctionComponent = ({ 17 | poolIndex, 18 | totalPool, 19 | poolName, 20 | pools, 21 | pool, 22 | setPoolSelected, 23 | selectedItem, 24 | handleSelectItems, 25 | handleSelectDuration, 26 | }) => { 27 | // const childPools: any[] = pool?.items; 28 | 29 | const handleSelect = (poolIndex: number, itemIndex: number, duration: number) => { 30 | const selectedPool = totalPool[poolIndex].est_apr[itemIndex]; 31 | 32 | const defaultPool = poolIndex === 0 ? totalPool[1]?.est_apr[0] : totalPool[0]?.est_apr[0]; 33 | 34 | const result = [ 35 | { 36 | pool: poolIndex, 37 | apr: selectedPool?.value || 0, 38 | }, 39 | { 40 | pool: poolIndex == 0 ? 1 : 0, 41 | apr: defaultPool?.value, 42 | }, 43 | ]; 44 | 45 | // setPoolSelected(result); 46 | }; 47 | 48 | return ( 49 |
50 | {pools?.map((pool: any, index: number) => { 51 | return ( 52 |
{ 55 | handleSelectItems(poolName, pool?.id); 56 | handleSelectDuration(poolIndex, pool?.time); 57 | handleSelect(poolIndex, index, pool?.time); 58 | e.stopPropagation(); 59 | }} 60 | className={classNames( 61 | 'relative flex justify-center items-center w-[32px] h-[32px] bg-[#393C46] border-[2px] border-[#393C46] rounded-[4px] text-[#fff] text-sm font-semibold cursor-pointer', 62 | { 63 | 'border-[2px] border-[#4A7DFF]': selectedItem?.contract_address == pool?.contract_address, 64 | }, 65 | )} 66 | > 67 | {pool?.time} 68 | {/**/} 73 |
74 | ); 75 | })} 76 |
77 | ); 78 | }; 79 | 80 | export default StakingPoolDuration; 81 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/StakingPoolTab/StakingPoolTabContent.tsx: -------------------------------------------------------------------------------- 1 | import StakingPoolItem from '@/common/components/Views/Staking/StakingPoolTab/StakingPoolItem'; 2 | import { useConnectModal } from '@rainbow-me/rainbowkit'; 3 | import { Col, Row } from 'antd'; 4 | import React, { useMemo } from 'react'; 5 | import { useAccount } from 'wagmi'; 6 | 7 | interface Props { 8 | token: any; 9 | dataSource: any[]; 10 | selectedItem: any; 11 | handleSelectItems: (poolName: any, selectedId: any) => void; 12 | selectedPool: any; 13 | selectedDurations: any; 14 | setShowModalStaking: (val: boolean) => void; 15 | setPoolIndex: (val: any) => void; 16 | setSelectedPool: (val: any) => void; 17 | setSelectedParentPool: (val: any) => void; 18 | handleSelectDuration: (poolId: number, duration: any) => void; 19 | infoPool: any; 20 | infoPool2: any; 21 | } 22 | 23 | const StakingPoolTabContent: React.FunctionComponent = ({ 24 | token, 25 | dataSource, 26 | selectedItem, 27 | handleSelectItems, 28 | selectedPool, 29 | selectedDurations, 30 | setShowModalStaking, 31 | setPoolIndex, 32 | setSelectedPool, 33 | setSelectedParentPool, 34 | handleSelectDuration, 35 | infoPool, 36 | infoPool2, 37 | }) => { 38 | const { address } = useAccount(); 39 | const { openConnectModal } = useConnectModal(); 40 | 41 | const tableData = useMemo(() => { 42 | return dataSource?.filter((item: any) => item?.pool_name != 'Simple Earn'); 43 | }, [dataSource]); 44 | 45 | return ( 46 |
47 |
48 | 49 | Pool 50 | 51 | APR 52 | 53 | 54 | Duration (months) 55 | 56 | Staking cap 57 | Staked amount 58 | 59 | 60 |
61 | {tableData?.map((item: any, index: number) => { 62 | return ( 63 |
{ 66 | if (!address) { 67 | if (openConnectModal) { 68 | openConnectModal(); 69 | } 70 | return; 71 | } 72 | setPoolIndex(index); 73 | setSelectedParentPool(item); 74 | // setShowModalStaking(true); 75 | }} 76 | > 77 | 93 |
94 | ); 95 | })} 96 |
97 |
98 |
99 | ); 100 | }; 101 | 102 | export default StakingPoolTabContent; 103 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/StakingReward.tsx: -------------------------------------------------------------------------------- 1 | import { formatRewardBalance } from '@/utils'; 2 | import Image from 'next/image'; 3 | import React from 'react'; 4 | 5 | interface Props { 6 | rewardUnice: any; 7 | rewardFrens: any; 8 | tokenPrice: number; 9 | frensPrice: number; 10 | } 11 | 12 | const StakingReward: React.FunctionComponent = ({ rewardUnice, rewardFrens, tokenPrice, frensPrice }) => { 13 | return ( 14 |
15 |
16 |
17 | {''} 18 |
UNICE
19 |
20 |
21 |
+{formatRewardBalance(rewardUnice, 4)}
22 |
23 | ~${formatRewardBalance(Number(rewardUnice) * tokenPrice, 4)} USDT 24 |
25 |
26 |
27 |
28 |
29 | {''} 30 |
FRENS
31 |
32 |
33 |
+{formatRewardBalance(rewardFrens, 4)}
34 |
35 | ~${formatRewardBalance(Number(rewardFrens) * frensPrice, 4)} USDT 36 |
37 |
38 |
39 |
40 | ); 41 | }; 42 | 43 | export default StakingReward; 44 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/StakingTab/index.tsx: -------------------------------------------------------------------------------- 1 | import InputCurrency from '@/common/components/common-components/InputCureency'; 2 | import StakingPoolAPR from '@/common/components/Views/Staking/StakingPoolTab/StakingPoolAPR'; 3 | import { tokenAddress } from '@/common/consts'; 4 | import tokenABI from '@/common/contracts/abis/token.json'; 5 | import { formatNumber } from '@/utils'; 6 | import { Button, Popover } from 'antd'; 7 | import classNames from 'classnames'; 8 | import Image from 'next/image'; 9 | import Link from 'next/link'; 10 | import React, { useMemo } from 'react'; 11 | import { useAccount, useClient, useReadContract } from 'wagmi'; 12 | 13 | interface Props { 14 | loading: boolean; 15 | poolName: string; 16 | totalPool: any; 17 | stakeInfo: StakeInfo; 18 | amount: string; 19 | listPool: any[]; 20 | selectedItem: any; 21 | selectedPool: any; 22 | poolInfo: any; 23 | poolAddress: string; 24 | setPoolAddress: (address: string) => void; 25 | validate: string; 26 | setValidate: (value: string) => void; 27 | balance: number | string; 28 | stakeToken: Token; 29 | onChangeAmount: (value: string) => void; 30 | handleStake: (pool: any, poolId: string) => void; 31 | setSelectedPool: (val: any) => void; 32 | handleSelectItems: (poolName: any, selectedId: any) => void; 33 | } 34 | 35 | const StakingTab: React.FunctionComponent = ({ 36 | totalPool, 37 | poolName, 38 | amount, 39 | listPool, 40 | poolInfo, 41 | selectedItem, 42 | selectedPool, 43 | setPoolAddress, 44 | validate, 45 | setValidate, 46 | balance, 47 | stakeToken, 48 | onChangeAmount, 49 | handleStake, 50 | setSelectedPool, 51 | handleSelectItems, 52 | }) => { 53 | const { address } = useAccount(); 54 | const client = useClient(); 55 | 56 | const { data: balanceToken = 0 } = useReadContract({ 57 | abi: tokenABI, 58 | address: tokenAddress, 59 | functionName: 'balanceOf', 60 | args: [address], 61 | chainId: client?.chain?.id ?? 1, 62 | }); 63 | 64 | const currentPool = useMemo(() => { 65 | return selectedItem?.find((item: any) => item?.pool_name == totalPool?.pool_name)?.item; 66 | }, [selectedItem, totalPool]); 67 | 68 | return ( 69 |
70 |
71 |
72 |
Duration (months)
73 |
74 | {listPool?.map((pool: any) => { 75 | return ( 76 |
{ 79 | handleSelectItems(poolName, pool?.id); 80 | setPoolAddress(pool?.contract_address); 81 | setSelectedPool(pool); 82 | // setPoolSelected(); 83 | }} 84 | className={classNames( 85 | 'w-[38px] sm:w-[41px] relative text-center bg-[#393C46] border border-[#393C46] rounded-[4px] text-base font-semibold py-[6px] sm:px-[12px] cursor-pointer', 86 | { 87 | 'border border-[#4A7DFF]': currentPool?.contract_address === pool?.contract_address, 88 | }, 89 | )} 90 | > 91 | {pool?.time} 92 | {''} 97 |
98 | ); 99 | })} 100 |
101 |
102 |
103 |
APR
104 | } 106 | title={'Staking APR'} 107 | > 108 |
109 | {Number(currentPool?.value[0] ?? 0) + Number(currentPool?.value[1] ?? 0)}% 110 | {''} 115 |
116 |
117 |
118 |
119 |
120 | { 130 | setValidate(''); 131 | onChangeAmount(value); 132 | }} 133 | loading={true} 134 | /> 135 |

{validate}

136 | 137 |
Get $UNICE
138 | 139 |
140 | 149 |
150 | ); 151 | }; 152 | 153 | export default StakingTab; 154 | -------------------------------------------------------------------------------- /src/common/components/Views/Staking/VipLevelDetails.tsx: -------------------------------------------------------------------------------- 1 | import { formatNumber } from '@/utils'; 2 | import { Row } from 'antd'; 3 | import Image from 'next/image'; 4 | import React from 'react'; 5 | 6 | interface Props { 7 | vipLevels: any; 8 | } 9 | 10 | const VipLevelDetails: React.FunctionComponent = ({ vipLevels }) => { 11 | return ( 12 |
13 | 14 |
15 |
Tier Levels
16 |
Voting power Required
17 |
Allocation
18 |
19 |
20 | {vipLevels?.map(({ name, amount }: any) => { 21 | return ( 22 |
23 | 28 |
29 | {name} 30 |
31 |
{formatNumber(amount)}
32 |
{formatNumber(amount)}
33 |
34 | ); 35 | })} 36 |
37 |
38 | 39 |
40 | 41 | 42 | 43 | 44 | {vipLevels?.map(({ name }: any) => { 45 | return ( 46 | 53 | ); 54 | })} 55 | 56 | 57 | 58 | {vipLevels?.map(({ name }: any) => { 59 | return ( 60 | 67 | ); 68 | })} 69 | 70 | 71 | 74 | {vipLevels?.map(({ name, amount }: any) => { 75 | return ; 76 | })} 77 | 78 | 79 |
47 | 52 |
VIP Levels 61 |
64 | {name} 65 |
66 |
72 | Voting power
Required 73 |
{formatNumber(amount)}
80 |
81 |
82 |
83 | ); 84 | }; 85 | 86 | export default VipLevelDetails; 87 | -------------------------------------------------------------------------------- /src/common/components/common-components/CustomProgress/index.tsx: -------------------------------------------------------------------------------- 1 | import { formatNumber } from '@/utils'; 2 | import { Progress } from 'antd'; 3 | import { useEffect } from 'react'; 4 | 5 | interface Props { 6 | progress: number; 7 | } 8 | 9 | const CustomProgress: React.FunctionComponent = ({ progress }) => { 10 | useEffect(() => { 11 | const progressBars = document.querySelectorAll('.ant-progress-bg'); 12 | progressBars.forEach((progressBar) => { 13 | // if (!progressBar.querySelector('.custom-div')) { 14 | const customDiv = document.createElement('div'); 15 | customDiv.className = 'custom-div'; 16 | customDiv.innerText = `${formatNumber(progress, 1)}%`; 17 | customDiv.style.position = 'absolute'; 18 | customDiv.style.height = '20px'; 19 | customDiv.style.display = 'flex'; 20 | customDiv.style.justifyContent = 'center'; 21 | customDiv.style.alignItems = 'center'; 22 | customDiv.style.top = '50%'; 23 | customDiv.style.right = '1px'; 24 | customDiv.style.transform = 'translate(0%, -50%)'; 25 | customDiv.style.padding = '4px 8px'; 26 | customDiv.style.color = '#000'; 27 | customDiv.style.fontSize = '12px'; 28 | customDiv.style.fontWeight = '700'; 29 | customDiv.style.backgroundColor = '#fff'; 30 | customDiv.style.borderRadius = '47px'; 31 | customDiv.style.border = '1px solid #1AC774'; 32 | customDiv.style.backgroundColor = '#fff'; 33 | progressBar.appendChild(customDiv); 34 | // } 35 | }); 36 | }, [progress]); 37 | 38 | return ; 39 | }; 40 | 41 | export default CustomProgress; 42 | -------------------------------------------------------------------------------- /src/common/components/common-components/DisableInput/index.tsx: -------------------------------------------------------------------------------- 1 | import PositiveFloatNumInput from '@/common/components/PositiveFloatNumInput'; 2 | import { useModal } from '@/common/hooks/useModal'; 3 | import { formatNumber, formatNumberBalance } from '@/utils'; 4 | import Image from 'next/image'; 5 | import React, { useMemo } from 'react'; 6 | import Skeleton from '../../Skeleton'; 7 | 8 | interface InputProps { 9 | max?: number; 10 | min?: number; 11 | handleChange?: (value: string) => void; 12 | amount: string; 13 | token: any; 14 | balance: any; 15 | subTitle?: string; 16 | setShowTokens?: (value: boolean) => void; 17 | disabled?: boolean; 18 | isOnSelected?: boolean; 19 | setTokenSelected?: any; 20 | enableUseMax?: boolean; 21 | label?: string; 22 | hiddenBalance?: boolean; 23 | className?: string; 24 | maxDecimals?: number; 25 | loading?: boolean; 26 | enableBalance?: string; 27 | } 28 | 29 | const MIN_AMOUNT_DEFAULT = 0.15; 30 | 31 | const DisableInput: React.FunctionComponent = ({ 32 | handleChange, 33 | amount, 34 | token, 35 | balance, 36 | subTitle, 37 | disabled = false, 38 | isOnSelected = true, 39 | loading = false, 40 | enableUseMax, 41 | label, 42 | hiddenBalance = false, 43 | maxDecimals = 8, 44 | className, 45 | max, 46 | enableBalance = '', 47 | }) => { 48 | const { setShow: setShowModalTokens } = useModal(); 49 | 50 | const useMaxBalance = () => { 51 | enableUseMax && !!handleChange && handleChange?.(String(MaxBalance)); 52 | }; 53 | 54 | const MaxBalance = useMemo(() => { 55 | const blBigIntUnit = balance * Math.pow(10, 8) - MIN_AMOUNT_DEFAULT * Math.pow(10, 8); 56 | if (token?.symbol === 'APT' && balance > 0.15) { 57 | return (blBigIntUnit / Math.pow(10, 8)).toFixed(token.decimals); 58 | } else if (token?.symbol === 'APT' && balance <= 0.15) { 59 | return 0; 60 | } else { 61 | return balance; 62 | } 63 | }, [balance, token]); 64 | 65 | const onChangeInput = (value: any) => { 66 | handleChange?.(value); 67 | }; 68 | 69 | return ( 70 |
71 | {label && ( 72 |
73 |
{label}
74 | {!hiddenBalance && ( 75 |
76 | {loading ? ( 77 | 78 | ) : ( 79 |
${formatNumberBalance(Number(amount) * token?.price, 4)}
80 | )} 81 |
82 | {subTitle ? subTitle : 'Balance'}: {''} 83 | {`${formatNumber(balance, 2)}`} 84 |
85 |
86 | )} 87 | {enableBalance == 'top' && ( 88 |
89 | 90 | Max: 91 | {`${formatNumber(balance, 2)}`} 92 | 93 |
94 | )} 95 |
96 | )} 97 |
98 | 99 | 109 | 110 | isOnSelected && setShowModalTokens(true)} 112 | className={'border-0 shadow-none flex items-center justify-center py-1 px-3 gap-2 cursor-pointer h-auto'} 113 | > 114 | {/*{token && token.symbol === 'MGPT' ? (*/} 115 | {/* // */} 120 | {/* */} 121 | {/*) : (*/} 122 | {/* */} 123 | {/*)}*/} 124 |
125 | {!token?.symbol && } 126 | {!!token?.symbol && ( 127 | <> 128 |
{token?.symbol}
129 | 130 | )} 131 |
132 |
133 |
134 | {enableBalance == 'bottom' && ( 135 |
136 | 137 | Max: 138 | {`${formatNumber(balance, 2)}`} 139 | 140 |
141 | )} 142 | 143 | {/**/} 148 |
149 | ); 150 | }; 151 | export default DisableInput; 152 | -------------------------------------------------------------------------------- /src/common/components/common-components/InputCureency/index.tsx: -------------------------------------------------------------------------------- 1 | import PositiveFloatNumInput from '@/common/components/PositiveFloatNumInput'; 2 | import { APTIcon } from '@/common/components/icon/common'; 3 | import { useModal } from '@/common/hooks/useModal'; 4 | import { formatNumber, formatNumberBalance } from '@/utils'; 5 | import Image from 'next/image'; 6 | import React, { useMemo } from 'react'; 7 | import Skeleton from '../../Skeleton'; 8 | 9 | interface InputProps { 10 | max?: number; 11 | min?: number; 12 | handleChange?: (value: string) => void; 13 | amount: string; 14 | token: any; 15 | balance: any; 16 | subTitle?: string; 17 | setShowTokens?: (value: boolean) => void; 18 | disabled?: boolean; 19 | isAllowDecimal?: boolean; 20 | isOnSelected?: boolean; 21 | setTokenSelected?: any; 22 | enableUseMax?: boolean; 23 | label?: string; 24 | hiddenBalance?: boolean; 25 | className?: string; 26 | maxDecimals?: number; 27 | loading?: boolean; 28 | enableBalance?: string; 29 | } 30 | 31 | const MIN_AMOUNT_DEFAULT = 0.15; 32 | 33 | const InputCurrency: React.FunctionComponent = ({ 34 | handleChange, 35 | amount, 36 | token, 37 | balance, 38 | subTitle, 39 | disabled = false, 40 | isAllowDecimal, 41 | isOnSelected = true, 42 | loading = false, 43 | enableUseMax, 44 | label, 45 | hiddenBalance = false, 46 | maxDecimals, 47 | className, 48 | max, 49 | enableBalance = '', 50 | }) => { 51 | const { setShow: setShowModalTokens } = useModal(); 52 | 53 | const useMaxBalance = () => { 54 | if (enableUseMax && !!handleChange) { 55 | const balanceString = String(MaxBalance); 56 | const [integerPart, decimalPart] = balanceString.split('.'); 57 | const truncatedBalance = decimalPart ? `${integerPart}.${decimalPart[0]}` : integerPart; 58 | handleChange(truncatedBalance); 59 | } 60 | }; 61 | 62 | const MaxBalance = useMemo(() => { 63 | const blBigIntUnit = balance * Math.pow(10, 8) - MIN_AMOUNT_DEFAULT * Math.pow(10, 8); 64 | if (token?.symbol === 'APT' && balance > 0.15) { 65 | return (blBigIntUnit / Math.pow(10, 8)).toFixed(token.decimals); 66 | } else if (token?.symbol === 'APT' && balance <= 0.15) { 67 | return 0; 68 | } else { 69 | return balance; 70 | } 71 | }, [balance, token]); 72 | 73 | const onChangeInput = (value: any) => { 74 | handleChange?.(value); 75 | }; 76 | 77 | return ( 78 |
79 | {label && ( 80 |
81 |
{label}
82 | {!hiddenBalance && ( 83 |
84 | {loading ? ( 85 | 86 | ) : ( 87 |
${formatNumberBalance(Number(amount) * token?.price, 4)}
88 | )} 89 |
90 | {subTitle ? subTitle : 'Balance'}: {''} 91 | {`${formatNumber(balance, 2)}`} 92 | 96 | Max 97 | 98 |
99 |
100 | )} 101 | {enableBalance == 'top' && ( 102 |
103 | 104 | Max: 105 | {`${formatNumber(balance, 2)}`} 106 | 107 |
108 | )} 109 |
110 | )} 111 |
112 | 113 | 124 | 125 | isOnSelected && setShowModalTokens(true)} 127 | className={ 128 | 'w-fit rounded-full bg-[#272830] flex items-center justify-center py-2 px-4 sm:px-5 gap-2 cursor-pointer h-auto' 129 | } 130 | > 131 | {token && token.symbol === 'UNICE' ? ( 132 | // 137 | {''} 138 | ) : ( 139 | 140 | )} 141 |
142 | {!token?.symbol && } 143 | {!!token?.symbol && ( 144 |
145 |
{token?.symbol}
146 |
147 | )} 148 |
149 |
150 |
151 | 152 | {enableBalance == 'bottom' && ( 153 |
154 | 155 | Max: 156 | {`${formatNumber(balance, 2)}`} 157 | 158 |
159 | )} 160 | 161 | {/**/} 166 |
167 | ); 168 | }; 169 | export default InputCurrency; 170 | -------------------------------------------------------------------------------- /src/common/components/common-components/MobileMenu/BottomMenu.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | position: fixed; 3 | bottom: 0; 4 | left: 0; 5 | right: 0; 6 | z-index: 100; 7 | text-align: center; 8 | border-radius: 16px 16px 0px 0px; 9 | border: 0px solid #E7E7E8; 10 | background: #FFF; 11 | box-shadow: 0px -3px 12px 0px rgba(0, 3, 28, 0.16); 12 | } 13 | 14 | @media (min-width: 768px) { 15 | .container { 16 | display: none; 17 | } 18 | } 19 | 20 | .toggleButton { 21 | width: 100%; 22 | } 23 | 24 | .menu { 25 | /*height: 0;*/ 26 | /*overflow: hidden;*/ 27 | /*transition: all .5s ease-in-out;*/ 28 | opacity: 0; 29 | } 30 | 31 | .visible { 32 | /*height: 300px;*/ 33 | /*opacity: 1;*/ 34 | } 35 | 36 | .blurOverlay { 37 | position: fixed; 38 | top: 0; 39 | left: 0; 40 | right: 0; 41 | bottom: 0; 42 | background-color: rgba(0, 0, 0, 0.5); 43 | backdrop-filter: blur(5px); 44 | z-index: -50; 45 | } 46 | 47 | .arrow { 48 | margin-left: 8px; 49 | transition: transform 0.3s ease; 50 | } 51 | 52 | .arrowUp { 53 | transform: rotate(360deg); 54 | } -------------------------------------------------------------------------------- /src/common/components/common-components/MobileMenu/index.tsx: -------------------------------------------------------------------------------- 1 | import { LargeArrowDownIcon, LargeArrowUpIcon } from '@/common/components/icon/common'; 2 | import React, { useEffect, useRef, useState } from 'react'; 3 | import { useSwipeable } from 'react-swipeable'; 4 | import styles from './BottomMenu.module.css'; 5 | 6 | const BottomMenu: React.FunctionComponent<{ campaign?: Campaign; childCmp: any; title: any; currentStep?: number }> = ({ 7 | campaign, 8 | childCmp, 9 | title, 10 | currentStep, 11 | }) => { 12 | const [visible, setVisible] = useState(false); 13 | const contentRef = useRef(null); 14 | const [contentHeight, setContentHeight] = useState('0px'); 15 | 16 | const toggleComponent = () => { 17 | setVisible((prev) => !prev); 18 | }; 19 | 20 | useEffect(() => { 21 | if (visible && contentRef.current) { 22 | setContentHeight(`${contentRef.current.scrollHeight}px`); 23 | } else { 24 | setContentHeight('0px'); 25 | } 26 | }, [visible]); 27 | 28 | const handlers = useSwipeable({ 29 | onSwipedUp: () => setVisible(true), 30 | onSwipedDown: () => setVisible(false), 31 | // preventDefaultTouchmoveEvent: true, 32 | trackMouse: true, 33 | }); 34 | 35 | return ( 36 |
37 |
43 | {title} 44 | 45 | {!visible ? : } 46 | 47 |
48 |
57 | {childCmp} 58 |
59 | {visible &&
setVisible(false)} />} 60 |
61 | ); 62 | }; 63 | 64 | export default BottomMenu; 65 | -------------------------------------------------------------------------------- /src/common/components/countdowns/CampaignTitle.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | 3 | const CampaignTittle = () => { 4 | return
; 5 | }; 6 | 7 | export default CampaignTittle; 8 | -------------------------------------------------------------------------------- /src/common/components/countdowns/CountdownActiveCampaign.tsx: -------------------------------------------------------------------------------- 1 | import { getData } from '@/common/hooks/useLocalStoragre'; 2 | import moment from 'moment'; 3 | import React, { useEffect, useMemo, useState } from 'react'; 4 | import { useDispatch, useSelector } from 'react-redux'; 5 | 6 | export interface Time { 7 | days: string; 8 | hours: string; 9 | minutes: string; 10 | seconds: string; 11 | } 12 | 13 | interface CountdownProps { 14 | campaign: any; 15 | onMomentChange: () => void; 16 | width?: number; 17 | height?: number; 18 | } 19 | 20 | const default_time: Time = { 21 | days: '00', 22 | hours: '00', 23 | minutes: '00', 24 | seconds: '00', 25 | }; 26 | 27 | const CountdownActiveCampaign: React.FunctionComponent = ({ campaign, onMomentChange }) => { 28 | const [time_left, setTimeLeft] = useState