├── .babelrc
├── .env.sample
├── .eslintrc.json
├── .github
└── workflows
│ ├── deploy.yml
│ └── lint_tsc.yml
├── .gitignore
├── .husky
└── pre-commit
├── .nvmrc
├── .prettierrc
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── cypress.json
├── cypress
├── integration
│ ├── add-liquidity.test.ts
│ ├── landing.test.ts
│ ├── lists.test.ts
│ ├── migrate-v1.test.ts
│ ├── pool.test.ts
│ ├── remove-liquidity.test.ts
│ ├── send.test.ts
│ ├── swap.test.ts
│ └── token-warning.ts
├── support
│ ├── commands.d.ts
│ ├── commands.js
│ └── index.js
└── tsconfig.json
├── index.html
├── package.json
├── patches
└── @web3-react+core+6.0.9.patch
├── public
├── favicon.png
├── images
│ ├── 192x192_App_Icon.png
│ ├── 384x384_App_Icon.png
│ └── flags
│ │ ├── de.svg
│ │ ├── en.svg
│ │ ├── es.svg
│ │ ├── fr.svg
│ │ ├── jp.svg
│ │ ├── pt-br.svg
│ │ ├── tr.svg
│ │ ├── vn.svg
│ │ └── zh.svg
└── manifest.json
├── scripts
└── build.js
├── src
├── apollo
│ ├── block.ts
│ ├── client.ts
│ ├── pair.ts
│ └── vote.ts
├── assets
│ ├── images
│ │ ├── beta-image.jpg
│ │ ├── giftbox.png
│ │ ├── gnosis_safe.png
│ │ ├── hashConnect.png
│ │ ├── logo.png
│ │ ├── migration_vector.png
│ │ ├── minus.png
│ │ ├── plus.png
│ │ └── xDefi.png
│ └── svg
│ │ ├── PNG
│ │ ├── PNG.svg
│ │ ├── PNG_EVMOS.svg
│ │ ├── PNG_HEDERA.svg
│ │ ├── PNR.svg
│ │ └── PSB.svg
│ │ ├── avalancheCore.svg
│ │ ├── backward.svg
│ │ ├── bitkeep.svg
│ │ ├── blue-loader.svg
│ │ ├── circleTick.svg
│ │ ├── coinbaseWalletIcon.svg
│ │ ├── discord.svg
│ │ ├── forward.svg
│ │ ├── lightMode.svg
│ │ ├── logoIcon.svg
│ │ ├── logoSloganDark.svg
│ │ ├── logoSloganLight.svg
│ │ ├── menu
│ │ ├── bridge.svg
│ │ ├── logout.svg
│ │ └── statatics.svg
│ │ ├── near.svg
│ │ ├── nightMode.svg
│ │ ├── social
│ │ ├── discord.svg
│ │ ├── github.svg
│ │ ├── medium.svg
│ │ ├── substack.svg
│ │ ├── telegram.svg
│ │ ├── twitter.svg
│ │ └── youtube.svg
│ │ ├── stake.svg
│ │ ├── transaction.svg
│ │ ├── unstake.svg
│ │ ├── wallet.svg
│ │ ├── walletConnectIcon.svg
│ │ └── x.svg
├── components
│ ├── AccountDetails
│ │ ├── Copy.tsx
│ │ ├── Transaction.tsx
│ │ ├── index.tsx
│ │ └── styled.tsx
│ ├── AccountDetailsModal
│ │ └── index.tsx
│ ├── Beta
│ │ ├── ComingSoon
│ │ │ ├── index.tsx
│ │ │ └── styled.ts
│ │ └── TransactionSubmitted
│ │ │ ├── index.tsx
│ │ │ └── styled.ts
│ ├── Button
│ │ └── index.tsx
│ ├── Card
│ │ └── index.tsx
│ ├── Column
│ │ └── index.tsx
│ ├── Confetti
│ │ └── index.tsx
│ ├── Header
│ │ ├── HederaPoolWarning.tsx
│ │ ├── Polling.tsx
│ │ └── URLWarning.tsx
│ ├── Icons
│ │ ├── Bridge.tsx
│ │ ├── Buy.tsx
│ │ ├── C14.tsx
│ │ ├── CoinbasePay.tsx
│ │ ├── Dashboard.tsx
│ │ ├── Logo.tsx
│ │ ├── MoonPay.tsx
│ │ ├── Pool.tsx
│ │ ├── Stake.tsx
│ │ ├── Swap.tsx
│ │ ├── Vote.tsx
│ │ └── index.ts
│ ├── Identicon
│ │ └── index.tsx
│ ├── Loader
│ │ └── index.tsx
│ ├── MigrationCard
│ │ ├── index.tsx
│ │ └── styleds.tsx
│ ├── MigrationModal
│ │ ├── ChoosePool
│ │ │ ├── PairData.tsx
│ │ │ ├── index.tsx
│ │ │ └── styleds.tsx
│ │ ├── Loader
│ │ │ ├── index.tsx
│ │ │ └── styleds.tsx
│ │ ├── PoolInfo
│ │ │ ├── index.tsx
│ │ │ └── styleds.tsx
│ │ ├── Stake
│ │ │ ├── index.tsx
│ │ │ └── styleds.tsx
│ │ ├── StepView
│ │ │ ├── index.tsx
│ │ │ └── styleds.tsx
│ │ ├── Unstake
│ │ │ ├── index.tsx
│ │ │ └── styleds.tsx
│ │ ├── index.tsx
│ │ └── styleds.tsx
│ ├── NewVersionModal
│ │ └── index.tsx
│ ├── Popups
│ │ ├── PopupItem.tsx
│ │ ├── TransactionPopup.tsx
│ │ └── index.tsx
│ ├── Row
│ │ └── index.tsx
│ ├── Stat
│ │ ├── index.tsx
│ │ └── styled.tsx
│ ├── StyledMenu
│ │ └── index.tsx
│ ├── SwitchSubgraph
│ │ └── index.tsx
│ ├── Web3ReactManager
│ │ └── index.tsx
│ ├── Web3Status
│ │ └── index.tsx
│ └── earn
│ │ └── styled.ts
├── constants
│ ├── Policies
│ │ ├── CPolicy.ts
│ │ ├── PrivacyPolicy.ts
│ │ └── TermsService.ts
│ ├── abis
│ │ └── staking-rewards.ts
│ ├── accessPermissions.ts
│ └── index.ts
├── hooks
│ ├── index.ts
│ ├── mixpanel.tsx
│ ├── useCopyClipboard.ts
│ ├── useTransactionDeadline.ts
│ └── useWindowSize.ts
├── index.tsx
├── layout
│ ├── Footer
│ │ ├── PolicyModal.tsx
│ │ ├── index.tsx
│ │ └── styled.ts
│ ├── Header
│ │ ├── MenuIcon.tsx
│ │ ├── MobileHeader.tsx
│ │ ├── MobileMenu
│ │ │ ├── MobileWeb3Status.tsx
│ │ │ ├── TransactionModal.tsx
│ │ │ ├── index.tsx
│ │ │ └── styled.ts
│ │ ├── index.tsx
│ │ └── styled.ts
│ ├── Logo
│ │ ├── index.tsx
│ │ └── styled.ts
│ ├── Sidebar
│ │ ├── MenuLinks.tsx
│ │ ├── NavItem.tsx
│ │ ├── index.tsx
│ │ └── styled.tsx
│ ├── SocialMedia
│ │ ├── index.tsx
│ │ └── styled.ts
│ ├── index.tsx
│ └── styled.ts
├── pages
│ ├── App.tsx
│ ├── Beta
│ │ ├── Bridge
│ │ │ ├── index.tsx
│ │ │ └── styleds.tsx
│ │ ├── Buy
│ │ │ ├── C14
│ │ │ │ └── index.tsx
│ │ │ ├── CoinbasePay
│ │ │ │ └── index.tsx
│ │ │ ├── Moonpay
│ │ │ │ └── index.tsx
│ │ │ ├── index.tsx
│ │ │ └── styled.tsx
│ │ ├── Governance
│ │ │ └── index.tsx
│ │ ├── GovernanceDetail
│ │ │ └── index.tsx
│ │ ├── Policy
│ │ │ ├── index.tsx
│ │ │ └── styled.ts
│ │ ├── Pool
│ │ │ └── index.tsx
│ │ ├── Stake
│ │ │ ├── ClaimDrawer
│ │ │ │ └── index.tsx
│ │ │ ├── ClaimWidget
│ │ │ │ ├── index.tsx
│ │ │ │ └── styled.tsx
│ │ │ ├── DetailModal
│ │ │ │ ├── Details
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styled.ts
│ │ │ │ ├── EarnedWidget
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styled.tsx
│ │ │ │ ├── Header
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styled.tsx
│ │ │ │ ├── StakeWidget
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styled.ts
│ │ │ │ ├── StatDetail
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styleds.tsx
│ │ │ │ ├── UnstakeDrawer
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styled.ts
│ │ │ │ ├── index.tsx
│ │ │ │ └── styled.tsx
│ │ │ ├── PoolCard
│ │ │ │ ├── StakeDrawer
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── StakeWidget
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styled.ts
│ │ │ │ ├── index.tsx
│ │ │ │ └── styleds.tsx
│ │ │ ├── RewardStakeDrawer
│ │ │ │ └── index.tsx
│ │ │ ├── index.tsx
│ │ │ └── styleds.tsx
│ │ └── Swap
│ │ │ ├── LimitOrderList
│ │ │ ├── CancelOrder
│ │ │ │ ├── index.tsx
│ │ │ │ └── styleds.tsx
│ │ │ ├── CancelOrderModal
│ │ │ │ ├── index.tsx
│ │ │ │ └── styleds.tsx
│ │ │ ├── LimitOrderDetail.tsx
│ │ │ ├── LimitOrderRow.tsx
│ │ │ ├── MobileLimitOrderRow.tsx
│ │ │ ├── index.tsx
│ │ │ └── styleds.tsx
│ │ │ ├── PairInfo
│ │ │ ├── PairChart
│ │ │ │ ├── index.tsx
│ │ │ │ └── styleds.tsx
│ │ │ ├── PairStat
│ │ │ │ ├── index.tsx
│ │ │ │ └── styleds.tsx
│ │ │ └── index.tsx
│ │ │ ├── SwapUI
│ │ │ ├── index.tsx
│ │ │ └── styleds.tsx
│ │ │ └── index.tsx
│ ├── Dashboard
│ │ ├── index.tsx
│ │ └── styleds.tsx
│ ├── Migrate
│ │ ├── Migrate.tsx
│ │ ├── index.tsx
│ │ └── styleds.tsx
│ └── SarStake
│ │ ├── StakeStat
│ │ ├── index.tsx
│ │ └── styleds.tsx
│ │ ├── index.tsx
│ │ └── styleds.tsx
├── react-app-env.d.ts
├── setupProxy.js
├── state
│ ├── application
│ │ ├── actions.ts
│ │ ├── hooks.ts
│ │ ├── reducer.test.ts
│ │ └── reducer.ts
│ ├── global
│ │ └── actions.ts
│ ├── index.ts
│ ├── migrate
│ │ └── hooks.ts
│ ├── multicall
│ │ ├── actions.test.ts
│ │ ├── actions.ts
│ │ ├── hooks.ts
│ │ ├── reducer.test.ts
│ │ ├── reducer.ts
│ │ ├── updater.test.ts
│ │ └── updater.tsx
│ ├── pair
│ │ ├── actions.ts
│ │ ├── hooks.ts
│ │ └── reducer.ts
│ ├── stake
│ │ ├── hooks.ts
│ │ ├── multiChainsHooks.ts
│ │ └── singleSideConfig.ts
│ ├── token
│ │ ├── actions.ts
│ │ ├── hooks.ts
│ │ └── reducer.ts
│ ├── transactions
│ │ ├── actions.ts
│ │ ├── hooks.tsx
│ │ ├── reducer.test.ts
│ │ ├── reducer.ts
│ │ ├── updater.test.ts
│ │ └── updater.tsx
│ ├── user
│ │ ├── actions.ts
│ │ ├── hooks.tsx
│ │ ├── reducer.test.ts
│ │ ├── reducer.ts
│ │ └── updater.tsx
│ └── watchlists
│ │ ├── actions.ts
│ │ └── reducer.ts
├── theme
│ ├── components.tsx
│ ├── index.tsx
│ └── styled.d.ts
├── utils
│ ├── getLibrary.ts
│ ├── index.test.ts
│ ├── index.ts
│ ├── retry.test.ts
│ └── retry.ts
└── vite-env.d.ts
├── tsconfig.json
├── vite.config.ts
├── wrangler.toml
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "babel-plugin-styled-components",
4 | "@babel/plugin-transform-runtime",
5 | "@babel/plugin-proposal-optional-chaining",
6 | "@babel/plugin-proposal-nullish-coalescing-operator"]
7 | }
8 |
--------------------------------------------------------------------------------
/.env.sample:
--------------------------------------------------------------------------------
1 | VITE_MOONPAY_PK=""
2 | VITE_COINBASE_PK=""
3 | VITE_SUBGRAPH_BASE_URL="https://api.thegraph.com/subgraphs/name/pangolindex"
4 | VITE_MIXPANEL=""
5 | VITE_HASURAKEY=""
6 | VITE_WALLETCONNECT_PROJECTID=""
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "parserOptions": {
4 | "ecmaVersion": 2020,
5 | "sourceType": "module",
6 | "ecmaFeatures": {
7 | // Allows for the parsing of JSX
8 | "jsx": true
9 | }
10 | },
11 | "ignorePatterns": ["node_modules/**/*"],
12 | "settings": {
13 | "react": {
14 | "version": "detect"
15 | }
16 | },
17 | "extends": [
18 | "plugin:react/recommended",
19 | "plugin:@typescript-eslint/recommended",
20 | "plugin:react-hooks/recommended",
21 | "prettier/@typescript-eslint",
22 | "plugin:prettier/recommended"
23 | ],
24 | "plugins": ["react", "react-hooks", "prettier"],
25 | "rules": {
26 | "react/jsx-filename-extension": [
27 | 1,
28 | {
29 | "extensions": [".js", ".jsx", ".ts", ".tsx"]
30 | }
31 | ],
32 | "@typescript-eslint/explicit-function-return-type": "off",
33 | "prettier/prettier": "error",
34 | "@typescript-eslint/no-explicit-any": "off",
35 | "@typescript-eslint/camelcase": "off",
36 | "@typescript-eslint/no-non-null-assertion": "off",
37 | "@typescript-eslint/no-use-before-define": "off",
38 | "@typescript-eslint/ban-types": "off",
39 | "@typescript-eslint/explicit-module-boundary-types": "off"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deployment
2 |
3 | env:
4 | VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
5 | VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
6 |
7 | on:
8 | push:
9 |
10 | jobs:
11 | Deploy:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout Repo
15 | uses: actions/checkout@v4
16 |
17 | - name: Setup node
18 | uses: actions/setup-node@v3
19 | with:
20 | node-version: 18.x
21 | cache: 'yarn'
22 |
23 | - name: Install Vercel CLI
24 | run: npm install --global vercel@latest
25 |
26 | ### Production Deployment ###
27 | - name: Pull Vercel Environment Information ( Production )
28 | if: github.ref == 'refs/heads/master'
29 | run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
30 |
31 | - name: Build Project Artifacts ( Production )
32 | if: github.ref == 'refs/heads/master'
33 | run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
34 |
35 | - name: Deploy Project Artifacts to Vercel ( Production )
36 | if: github.ref == 'refs/heads/master'
37 | run: vercel deploy --prod --prebuilt --token=${{ secrets.VERCEL_TOKEN }}
38 |
39 | ### Preview Deployment ###
40 | - name: Pull Vercel Environment Information ( Preview )
41 | if: github.ref != 'refs/heads/master'
42 | run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
43 |
44 | - name: Build Project Artifacts ( Preview )
45 | if: github.ref != 'refs/heads/master'
46 | run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
47 |
48 | - name: Deploy Project Artifacts to Vercel ( Preview )
49 | if: github.ref != 'refs/heads/master'
50 | run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}
51 |
--------------------------------------------------------------------------------
/.github/workflows/lint_tsc.yml:
--------------------------------------------------------------------------------
1 | name: Linting & Typescript Cheking
2 |
3 | env:
4 | CI: true
5 |
6 | on:
7 | pull_request:
8 |
9 | jobs:
10 | lint_tsc:
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - uses: actions/checkout@v2
15 | with:
16 | # we actually need "github.event.pull_request.commits + 1" commit
17 | fetch-depth: 0
18 |
19 | - uses: actions/setup-node@v3
20 | with:
21 | node-version: '18.x'
22 | cache: 'yarn'
23 | registry-url: 'https://registry.npmjs.org/'
24 |
25 | - run: yarn --frozen-lockfile
26 | - run: yarn type-check
27 | - run: yarn lint
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | /.netlify
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | notes.txt
27 | .idea/
28 |
29 | .vscode/
30 |
31 | package-lock.json
32 |
33 |
34 | cypress/videos
35 | cypress/screenshots
36 | cypress/fixtures/example.json
37 | # Local Netlify folder
38 | .netlify
39 | .yalc
40 | yalc.lock
41 |
42 | dist
43 | .vercel
44 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npm run pre-commit-hook
5 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v18
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "printWidth": 120
5 | }
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pangolin Interface
2 |
3 | An open source interface for Pangolin -- a community-driven decentralized exchange for Avalanche and Ethereum assets with fast settlement, low transaction fees, and a democratic distribution -- powered by Avalanche.
4 |
5 | - Website: [pangolin.exchange](https://pangolin.exchange/)
6 | - Interface: [app.pangolin.exchange](https://app.pangolin.exchange)
7 | - Telegram: [Pangolin](https://t.me/pangolindexV2)
8 | - Discord: [Pangolin](https://discord.com/invite/CZttnRaYjK)
9 | - Twitter: [@pangolindex](https://twitter.com/pangolindex)
10 |
11 |
12 |
13 | ## Accessing the Pangolin Interface
14 |
15 | Visit [app.pangolin.exchange](https://app.pangolin.exchange).
16 |
17 | ## Development
18 |
19 | ### Install Dependencies
20 |
21 | ```bash
22 | yarn
23 | ```
24 |
25 |
26 | ### Run
27 |
28 | ```bash
29 | yarn start
30 | ```
31 |
32 | ### Configuring the environment (optional)
33 |
34 | To have the interface default to a different network when a wallet is not connected:
35 |
36 | 1. Make a copy of `.env.sample` named `.env`
37 |
38 | Note that the interface only works on testnets where both
39 | [Pangolin](https://github.com/pangolindex/exchange-contracts) and
40 | [multicall](https://github.com/makerdao/multicall) are deployed.
41 | The interface will not work on other networks.
42 |
43 | ## Attribution
44 | This code was adapted from this Uniswap repo: [uniswap-interface](https://github.com/Uniswap/uniswap-interface)
45 |
--------------------------------------------------------------------------------
/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "http://localhost:3000",
3 | "pluginsFile": false,
4 | "fixturesFolder": false,
5 | "supportFile": "cypress/support/index.js",
6 | "video": false,
7 | "defaultCommandTimeout": 10000
8 | }
9 |
--------------------------------------------------------------------------------
/cypress/integration/landing.test.ts:
--------------------------------------------------------------------------------
1 | import { TEST_ADDRESS_NEVER_USE_SHORTENED } from '../support/commands'
2 |
3 | describe('Landing Page', () => {
4 | beforeEach(() => cy.visit('/'))
5 | it('loads swap page', () => {
6 | cy.get('#swap-page')
7 | })
8 |
9 | it('redirects to url /swap', () => {
10 | cy.url().should('include', '/swap')
11 | })
12 |
13 | it('allows navigation to pool', () => {
14 | cy.get('#pool-nav-link').click()
15 | cy.url().should('include', '/pool')
16 | })
17 |
18 | it('is connected', () => {
19 | cy.get('#web3-status-connected').click()
20 | cy.get('#web3-account-identifier-row').contains(TEST_ADDRESS_NEVER_USE_SHORTENED)
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/cypress/integration/lists.test.ts:
--------------------------------------------------------------------------------
1 | describe('Lists', () => {
2 | beforeEach(() => {
3 | cy.visit('/swap')
4 | })
5 |
6 | it('defaults to uniswap list', () => {
7 | cy.get('#swap-currency-output .open-currency-select-button').click()
8 | cy.get('#currency-search-selected-list-name').should('contain', 'Uniswap')
9 | })
10 |
11 | it('change list', () => {
12 | cy.get('#swap-currency-output .open-currency-select-button').click()
13 | cy.get('#currency-search-change-list-button').click()
14 | cy.get('#list-row-tokens-1inch-eth .select-button').click()
15 | cy.get('#currency-search-selected-list-name').should('contain', '1inch')
16 | cy.get('#currency-search-change-list-button').click()
17 | cy.get('#list-row-tokens-uniswap-eth .select-button').click()
18 | cy.get('#currency-search-selected-list-name').should('contain', 'Uniswap')
19 | })
20 | })
21 |
--------------------------------------------------------------------------------
/cypress/integration/migrate-v1.test.ts:
--------------------------------------------------------------------------------
1 | describe('Migrate V1 Liquidity', () => {
2 | describe('Remove V1 liquidity', () => {
3 | it('renders the correct page', () => {
4 | cy.visit('/remove/v1/0x93bB63aFe1E0180d0eF100D774B473034fd60C36')
5 | cy.get('#remove-v1-exchange').should('contain', 'MKR/ETH')
6 | })
7 | })
8 | })
9 |
--------------------------------------------------------------------------------
/cypress/integration/pool.test.ts:
--------------------------------------------------------------------------------
1 | describe('Pool', () => {
2 | beforeEach(() => cy.visit('/pool'))
3 | it('add liquidity links to /add/ETH', () => {
4 | cy.get('#join-pool-button').click()
5 | cy.url().should('contain', '/add/ETH')
6 | })
7 |
8 | it('import pool links to /import', () => {
9 | cy.get('#import-pool-link').click()
10 | cy.url().should('contain', '/find')
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/cypress/integration/remove-liquidity.test.ts:
--------------------------------------------------------------------------------
1 | describe('Remove Liquidity', () => {
2 | it('redirects', () => {
3 | cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
4 | cy.url().should(
5 | 'contain',
6 | '/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
7 | )
8 | })
9 |
10 | it('eth remove', () => {
11 | cy.visit('/remove/ETH/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
12 | cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'ETH')
13 | cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
14 | })
15 |
16 | it('eth remove swap order', () => {
17 | cy.visit('/remove/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/ETH')
18 | cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'MKR')
19 | cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'ETH')
20 | })
21 |
22 | it('loads the two correct tokens', () => {
23 | cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
24 | cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
25 | cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
26 | })
27 |
28 | it('does not crash if ETH is duplicated', () => {
29 | cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xc778417E063141139Fce010982780140Aa0cD5Ab')
30 | cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
31 | cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'WETH')
32 | })
33 |
34 | it('token not in storage is loaded', () => {
35 | cy.visit('/remove/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
36 | cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'SKL')
37 | cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
38 | })
39 | })
40 |
--------------------------------------------------------------------------------
/cypress/integration/send.test.ts:
--------------------------------------------------------------------------------
1 | describe('Send', () => {
2 | it('should redirect', () => {
3 | cy.visit('/send')
4 | cy.url().should('include', '/swap')
5 | })
6 |
7 | it('should redirect with url params', () => {
8 | cy.visit('/send?outputCurrency=ETH&recipient=bob.argent.xyz')
9 | cy.url().should('contain', '/swap?outputCurrency=ETH&recipient=bob.argent.xyz')
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/cypress/integration/token-warning.ts:
--------------------------------------------------------------------------------
1 | describe('Warning', () => {
2 | beforeEach(() => {
3 | cy.visit('/swap?outputCurrency=0x0a40f26d74274b7f22b28556a27b35d97ce08e0a')
4 | })
5 |
6 | it('Check that warning is displayed', () => {
7 | cy.get('.token-warning-container').should('be.visible')
8 | })
9 |
10 | it('Check that warning hides after button dismissal', () => {
11 | cy.get('.token-dismiss-button').should('be.disabled')
12 | cy.get('.understand-checkbox').click()
13 | cy.get('.token-dismiss-button').should('not.be.disabled')
14 | cy.get('.token-dismiss-button').click()
15 | cy.get('.token-warning-container').should('not.be.visible')
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/cypress/support/commands.d.ts:
--------------------------------------------------------------------------------
1 | export const TEST_ADDRESS_NEVER_USE: string
2 |
3 | export const TEST_ADDRESS_NEVER_USE_SHORTENED: string
4 |
5 | // declare namespace Cypress {
6 | // // eslint-disable-next-line @typescript-eslint/class-name-casing
7 | // interface cy {
8 | // additionalCommands(): void
9 | // }
10 | // }
11 |
--------------------------------------------------------------------------------
/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This file is processed and loaded automatically before your test files.
3 | //
4 | // You can read more here:
5 | // https://on.cypress.io/configuration
6 | // ***********************************************************
7 |
8 | // Import commands.ts using ES2015 syntax:
9 | import './commands'
10 |
--------------------------------------------------------------------------------
/cypress/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "baseUrl": "../node_modules",
5 | "target": "es5",
6 | "lib": ["es5", "dom"],
7 | "types": ["cypress"]
8 | },
9 | "include": ["**/*.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 | Pangolin
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/patches/@web3-react+core+6.0.9.patch:
--------------------------------------------------------------------------------
1 | diff --git a/node_modules/@web3-react/core/dist/core.esm.js b/node_modules/@web3-react/core/dist/core.esm.js
2 | index e5bea1e..55f37ff 100644
3 | --- a/node_modules/@web3-react/core/dist/core.esm.js
4 | +++ b/node_modules/@web3-react/core/dist/core.esm.js
5 | @@ -179,18 +179,20 @@ var augmentConnectorUpdate = function augmentConnectorUpdate(connector, update)
6 | return Promise.resolve(Promise.all([update.chainId === undefined ? connector.getChainId() : update.chainId, update.account === undefined ? connector.getAccount() : update.account])).then(function (_ref2) {
7 | var _chainId = _ref2[0],
8 | _account = _ref2[1];
9 | - var chainId = normalizeChainId(_chainId);
10 | +
11 | + var chainId = connector.normalizeChainId === false ? _chainId : normalizeChainId(_chainId);
12 |
13 | if (!!connector.supportedChainIds && !connector.supportedChainIds.includes(chainId)) {
14 | throw new UnsupportedChainIdError(chainId, connector.supportedChainIds);
15 | }
16 |
17 | - var account = _account === null ? _account : normalizeAccount(_account);
18 | + var account = _account === null ? _account : connector.normalizeAccount === false ? _account : normalizeAccount(_account);
19 | return {
20 | provider: provider,
21 | chainId: chainId,
22 | account: account
23 | };
24 | +
25 | });
26 | };
27 |
28 |
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangolindex/interface/22f3eb07b757a9306a7e1c81eddeb1fa392edd23/public/favicon.png
--------------------------------------------------------------------------------
/public/images/192x192_App_Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangolindex/interface/22f3eb07b757a9306a7e1c81eddeb1fa392edd23/public/images/192x192_App_Icon.png
--------------------------------------------------------------------------------
/public/images/384x384_App_Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangolindex/interface/22f3eb07b757a9306a7e1c81eddeb1fa392edd23/public/images/384x384_App_Icon.png
--------------------------------------------------------------------------------
/public/images/flags/de.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/images/flags/fr.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/images/flags/jp.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/images/flags/tr.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/images/flags/vn.svg:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Pangolin",
3 | "name": "Pangolin",
4 | "description": "Swap or provide liquidity on the Pangolin Exchange",
5 | "iconPath": "./images/384x384_App_Icon.png",
6 | "icons": [
7 | {
8 | "src": "./images/192x192_App_Icon.png",
9 | "sizes": "192x192",
10 | "type": "image/png",
11 | "purpose": "any maskable"
12 | },
13 | {
14 | "src": "./images/512x512_App_Icon.png",
15 | "sizes": "512x512",
16 | "type": "image/png",
17 | "purpose": "any maskable"
18 | }
19 | ],
20 | "start_url": ".",
21 | "orientation": "portrait",
22 | "display": "standalone",
23 | "theme_color": "#FF6B00",
24 | "background_color": "#fff"
25 | }
26 |
--------------------------------------------------------------------------------
/scripts/build.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | const { spawn } = require('child_process')
3 |
4 | // console.log('setting NODE_OPTIONS to --max-old-space-size=8192')
5 | // Step 1: Set the environment variable
6 | // process.env.NODE_OPTIONS = '--max-old-space-size=8192'
7 |
8 | // Step 2: Run the "yarn build" command
9 | const yarnBuild = spawn('yarn', ['vite:build'], {
10 | stdio: 'inherit', // This option will pipe the stdio of the child process to the parent, effectively displaying it in the terminal.
11 | shell: true, // This option will run the command in a shell, allowing for environment variable expansion.
12 | env: process.env // Pass the modified environment variables to the child process.
13 | })
14 |
15 | // Step 4: Handle errors (though with 'stdio: inherit', errors will be displayed automatically)
16 | yarnBuild.on('error', error => {
17 | console.error(`Error executing "yarn build": ${error}`)
18 | })
19 |
20 | yarnBuild.on('close', code => {
21 | if (code !== 0) {
22 | console.error(`"yarn build" exited with code ${code}`)
23 | console.log('spawning another build')
24 | spawn('yarn', ['vite:build'], {
25 | stdio: 'inherit', // This option will pipe the stdio of the child process to the parent, effectively displaying it in the terminal.
26 | shell: true, // This option will run the command in a shell, allowing for environment variable expansion.
27 | env: process.env // Pass the modified environment variables to the child process.
28 | })
29 | }
30 | })
31 | /* eslint-enable */
32 |
--------------------------------------------------------------------------------
/src/apollo/block.ts:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag'
2 |
3 | export const GET_BLOCK = gql`
4 | query blocks($timestampFrom: Int!, $timestampTo: Int!) {
5 | blocks(
6 | first: 1
7 | orderBy: timestamp
8 | orderDirection: asc
9 | where: { timestamp_gt: $timestampFrom, timestamp_lt: $timestampTo }
10 | ) {
11 | id
12 | number
13 | timestamp
14 | }
15 | }
16 | `
17 |
18 | export const GET_BLOCKS = (timestamps: Array) => {
19 | let queryString = 'query blocks {'
20 | queryString += timestamps.map(timestamp => {
21 | return `t${timestamp}:blocks(first: 1, orderBy: timestamp, orderDirection: asc, where: { timestamp_gt: ${timestamp}, timestamp_lt: ${timestamp +
22 | 60 * 60 * 24 * 7} }) {
23 | number
24 | }`
25 | })
26 | queryString += '}'
27 | return gql(queryString)
28 | }
29 |
30 | export const PRICES_BY_BLOCK = (tokenAddress: string, blocks: Array) => {
31 | let queryString = 'query blocks {'
32 | queryString += blocks.map(
33 | block => `
34 | t${block.timestamp}:token(id:"${tokenAddress}", block: { number: ${block.number} }) {
35 | derivedETH
36 | }
37 | `
38 | )
39 | queryString += ','
40 | queryString += blocks.map(
41 | block => `
42 | b${block.timestamp}: bundle(id:"1", block: { number: ${block.number} }) {
43 | ethPrice
44 | }
45 | `
46 | )
47 |
48 | queryString += '}'
49 | return gql(queryString)
50 | }
51 |
--------------------------------------------------------------------------------
/src/apollo/client.ts:
--------------------------------------------------------------------------------
1 | import { ApolloClient } from 'apollo-client'
2 | import { InMemoryCache, NormalizedCacheObject } from 'apollo-cache-inmemory'
3 | import { HttpLink } from 'apollo-link-http'
4 | import { SUBGRAPH_BASE_URL } from 'src/constants'
5 | import { ChainId } from '@pangolindex/sdk'
6 |
7 | export const client = new ApolloClient({
8 | link: new HttpLink({
9 | uri: `${SUBGRAPH_BASE_URL}/exchange`
10 | }),
11 | cache: new InMemoryCache()
12 | })
13 |
14 | export const governanceClient = new ApolloClient({
15 | link: new HttpLink({
16 | uri: `${SUBGRAPH_BASE_URL}/governance`
17 | }),
18 | cache: new InMemoryCache()
19 | })
20 |
21 | export const avalancheBlockClient = new ApolloClient({
22 | link: new HttpLink({
23 | uri: 'https://api.thegraph.com/subgraphs/name/dasconnor/avalanche-blocks'
24 | }),
25 | cache: new InMemoryCache()
26 | })
27 |
28 | export const blockClients: { [chainId in ChainId]: ApolloClient | undefined } = {
29 | [ChainId.AVALANCHE]: avalancheBlockClient,
30 | [ChainId.FUJI]: undefined,
31 | [ChainId.WAGMI]: undefined,
32 | [ChainId.COSTON]: undefined,
33 | [ChainId.SONGBIRD]: undefined,
34 | [ChainId.FLARE_MAINNET]: undefined,
35 | [ChainId.HEDERA_TESTNET]: undefined,
36 | [ChainId.HEDERA_MAINNET]: undefined,
37 | [ChainId.NEAR_MAINNET]: undefined,
38 | [ChainId.NEAR_TESTNET]: undefined,
39 | [ChainId.COSTON2]: undefined,
40 | [ChainId.EVMOS_TESTNET]: undefined,
41 | [ChainId.EVMOS_MAINNET]: undefined,
42 | [ChainId.ETHEREUM]: undefined,
43 | [ChainId.POLYGON]: undefined,
44 | [ChainId.FANTOM]: undefined,
45 | [ChainId.XDAI]: undefined,
46 | [ChainId.BSC]: undefined,
47 | [ChainId.ARBITRUM]: undefined,
48 | [ChainId.CELO]: undefined,
49 | [ChainId.OKXCHAIN]: undefined,
50 | [ChainId.VELAS]: undefined,
51 | [ChainId.AURORA]: undefined,
52 | [ChainId.CRONOS]: undefined,
53 | [ChainId.FUSE]: undefined,
54 | [ChainId.MOONRIVER]: undefined,
55 | [ChainId.MOONBEAM]: undefined,
56 | [ChainId.OP]: undefined,
57 | [ChainId.SKALE_BELLATRIX_TESTNET]: undefined
58 | }
59 |
--------------------------------------------------------------------------------
/src/apollo/pair.ts:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag'
2 |
3 | export const HOURLY_PAIR_RATES = (pairAddress: string, blocks: Array) => {
4 | let queryString = 'query blocks {'
5 | queryString += blocks.map(
6 | block => `
7 | t${block.timestamp}: pair(id:"${pairAddress}", block: { number: ${block.number} }) {
8 | token0Price
9 | token1Price
10 | }
11 | `
12 | )
13 |
14 | queryString += '}'
15 | return gql(queryString)
16 | }
17 |
--------------------------------------------------------------------------------
/src/apollo/vote.ts:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag'
2 |
3 | export const GET_PROPOSALS = gql`
4 | query proposals($where: Proposal_filter) {
5 | proposals(orderBy: startTime, orderDirection: desc, where: $where) {
6 | id
7 | description
8 | eta
9 | startTime
10 | endTime
11 | proposer
12 | calldatas
13 | signatures
14 | forVotes
15 | againstVotes
16 | canceled
17 | executed
18 | targets
19 | }
20 | }
21 | `
22 |
--------------------------------------------------------------------------------
/src/assets/images/beta-image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangolindex/interface/22f3eb07b757a9306a7e1c81eddeb1fa392edd23/src/assets/images/beta-image.jpg
--------------------------------------------------------------------------------
/src/assets/images/giftbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangolindex/interface/22f3eb07b757a9306a7e1c81eddeb1fa392edd23/src/assets/images/giftbox.png
--------------------------------------------------------------------------------
/src/assets/images/gnosis_safe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangolindex/interface/22f3eb07b757a9306a7e1c81eddeb1fa392edd23/src/assets/images/gnosis_safe.png
--------------------------------------------------------------------------------
/src/assets/images/hashConnect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangolindex/interface/22f3eb07b757a9306a7e1c81eddeb1fa392edd23/src/assets/images/hashConnect.png
--------------------------------------------------------------------------------
/src/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangolindex/interface/22f3eb07b757a9306a7e1c81eddeb1fa392edd23/src/assets/images/logo.png
--------------------------------------------------------------------------------
/src/assets/images/migration_vector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangolindex/interface/22f3eb07b757a9306a7e1c81eddeb1fa392edd23/src/assets/images/migration_vector.png
--------------------------------------------------------------------------------
/src/assets/images/minus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangolindex/interface/22f3eb07b757a9306a7e1c81eddeb1fa392edd23/src/assets/images/minus.png
--------------------------------------------------------------------------------
/src/assets/images/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangolindex/interface/22f3eb07b757a9306a7e1c81eddeb1fa392edd23/src/assets/images/plus.png
--------------------------------------------------------------------------------
/src/assets/images/xDefi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangolindex/interface/22f3eb07b757a9306a7e1c81eddeb1fa392edd23/src/assets/images/xDefi.png
--------------------------------------------------------------------------------
/src/assets/svg/PNG/PNG.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/svg/avalancheCore.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/svg/backward.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/svg/bitkeep.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/svg/blue-loader.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/svg/discord.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/svg/forward.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/svg/logoIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/svg/menu/logout.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/svg/menu/statatics.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/svg/near.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/svg/nightMode.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/svg/social/discord.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
23 |
--------------------------------------------------------------------------------
/src/assets/svg/social/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
--------------------------------------------------------------------------------
/src/assets/svg/social/medium.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/svg/social/substack.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
--------------------------------------------------------------------------------
/src/assets/svg/social/telegram.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/svg/social/twitter.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/svg/social/youtube.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/svg/stake.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
28 |
--------------------------------------------------------------------------------
/src/assets/svg/transaction.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/svg/unstake.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
28 |
--------------------------------------------------------------------------------
/src/assets/svg/wallet.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/svg/x.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/AccountDetails/Copy.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import useCopyClipboard from '../../hooks/useCopyClipboard'
4 | import { LinkStyledButton } from '../../theme'
5 | import { CheckCircle, Copy } from 'react-feather'
6 | import { useTranslation } from '@honeycomb-finance/shared'
7 |
8 | const CopyIcon = styled(LinkStyledButton)`
9 | color: ${({ theme }) => theme.text3};
10 | flex-shrink: 0;
11 | display: flex;
12 | text-decoration: none;
13 | font-size: 0.825rem;
14 | :hover,
15 | :active,
16 | :focus {
17 | text-decoration: none;
18 | color: ${({ theme }) => theme.text2};
19 | }
20 | `
21 | const TransactionStatusText = styled.span`
22 | margin-left: 0.25rem;
23 | font-size: 0.825rem;
24 | ${({ theme }) => theme.flexRowNoWrap};
25 | align-items: center;
26 | `
27 |
28 | export default function CopyHelper(props: {
29 | toCopy: string
30 | children?: React.ReactNode
31 | size?: string
32 | color?: string
33 | }) {
34 | const [isCopied, setCopied] = useCopyClipboard()
35 | const { t } = useTranslation()
36 |
37 | return (
38 | setCopied(props.toCopy)}>
39 | {isCopied ? (
40 |
41 |
42 | {t('accountDetails.copied')}
43 |
44 | ) : (
45 |
46 |
47 |
48 | )}
49 | {isCopied ? '' : props.children}
50 |
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/AccountDetailsModal/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import AccountDetails from '../AccountDetails'
4 | import { Modal } from '@honeycomb-finance/core'
5 |
6 | const Wrapper = styled.div`
7 | ${({ theme }) => theme.flexColumnNoWrap}
8 | margin: 0;
9 | padding: 0;
10 | width: 100%;
11 | `
12 |
13 | export default function AccountDetailsModal({
14 | pendingTransactions,
15 | confirmedTransactions,
16 | ENSName,
17 | open,
18 | closeModal,
19 | onWalletChange
20 | }: {
21 | pendingTransactions: string[] // hashes of pending
22 | confirmedTransactions: string[] // hashes of confirmed
23 | ENSName?: string
24 | open: boolean
25 | closeModal: () => void
26 | onWalletChange: () => void
27 | }) {
28 | const getModalContent = () => {
29 | return (
30 |
37 | )
38 | }
39 |
40 | return (
41 |
42 | {getModalContent()}
43 |
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/Beta/ComingSoon/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useTranslation } from '@honeycomb-finance/shared'
3 | import { Text } from '@honeycomb-finance/core'
4 |
5 | import { Wrapper } from './styled'
6 |
7 | const ComingSoon = () => {
8 | const { t } = useTranslation()
9 |
10 | return (
11 |
12 |
13 | {t('swapPage.comingSoon')}
14 |
15 |
16 | )
17 | }
18 | export default ComingSoon
19 |
--------------------------------------------------------------------------------
/src/components/Beta/ComingSoon/styled.ts:
--------------------------------------------------------------------------------
1 | import { Box } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const Wrapper = styled(Box)`
5 | display: flex;
6 | align-items: center;
7 | justify-content: center;
8 | width: 100%;
9 | height: 100%;
10 | border-radius: 10px;
11 | padding: 10px;
12 | min-height: 350px;
13 | background-color: ${({ theme }) => theme.bg2};
14 | `
15 |
--------------------------------------------------------------------------------
/src/components/Beta/TransactionSubmitted/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { Box, Text, Button } from '@honeycomb-finance/core'
3 | import { useTranslation, getEtherscanLink } from '@honeycomb-finance/shared'
4 | import { Root, Link } from './styled'
5 | import { ArrowUpCircle } from 'react-feather'
6 | import { useChainId } from 'src/hooks'
7 | import { ThemeContext } from 'styled-components'
8 |
9 | interface Props {
10 | onClose: () => void
11 | hash?: string
12 | }
13 |
14 | const TransactionSubmitted = ({ onClose, hash }: Props) => {
15 | const chainId = useChainId()
16 | const { t } = useTranslation()
17 | const theme = useContext(ThemeContext)
18 |
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 | {t('earn.transactionSubmitted')}
27 |
28 | {chainId && hash && (
29 |
36 | {t('transactionConfirmation.viewExplorer')}
37 |
38 | )}
39 |
40 |
43 |
44 | )
45 | }
46 | export default TransactionSubmitted
47 |
--------------------------------------------------------------------------------
/src/components/Beta/TransactionSubmitted/styled.ts:
--------------------------------------------------------------------------------
1 | import { Box, Text } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const Root = styled(Box)`
5 | display: flex;
6 | flex-direction: column;
7 | height: 100%;
8 | padding: 10px;
9 | `
10 | export const Link = styled(Text)`
11 | text-decoration: none;
12 | color: ${({ theme }) => theme.blue1};
13 | `
14 |
--------------------------------------------------------------------------------
/src/components/Card/index.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Box } from 'rebass/styled-components'
3 |
4 | const Card = styled(Box)<{ padding?: string; border?: string; borderRadius?: string }>`
5 | width: 100%;
6 | border-radius: 16px;
7 | padding: 1.25rem;
8 | padding: ${({ padding }) => padding};
9 | border: ${({ border }) => border};
10 | border-radius: ${({ borderRadius }) => borderRadius};
11 | `
12 | export default Card
13 |
14 | export const BlackCard = styled(Card)`
15 | background-color: ${({ theme }) => theme.bg2};
16 | color: ${({ theme }) => theme.text1};
17 | `
18 |
--------------------------------------------------------------------------------
/src/components/Column/index.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const Column = styled.div`
4 | display: flex;
5 | flex-direction: column;
6 | justify-content: flex-start;
7 | `
8 | export const ColumnCenter = styled(Column)`
9 | width: 100%;
10 | align-items: center;
11 | `
12 |
13 | export const AutoColumn = styled.div<{
14 | gap?: 'sm' | 'md' | 'lg' | string
15 | justify?: 'stretch' | 'center' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'space-between'
16 | }>`
17 | display: grid;
18 | grid-auto-rows: auto;
19 | grid-row-gap: ${({ gap }) => (gap === 'sm' && '8px') || (gap === 'md' && '12px') || (gap === 'lg' && '24px') || gap};
20 | justify-items: ${({ justify }) => justify && justify};
21 | `
22 |
--------------------------------------------------------------------------------
/src/components/Confetti/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactConfetti from 'react-confetti'
3 | import { useWindowSize } from '../../hooks/useWindowSize'
4 |
5 | // eslint-disable-next-line react/prop-types
6 | export default function Confetti({ start, variant }: { start: boolean; variant?: string }) {
7 | const { width, height } = useWindowSize()
8 |
9 | const _variant = variant ? variant : height && width && height > 1.5 * width ? 'bottom' : variant
10 |
11 | return start && width && height ? (
12 |
31 | ) : null
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/Header/HederaPoolWarning.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useState } from 'react'
2 | import styled from 'styled-components'
3 | import { AlertTriangle, X } from 'react-feather'
4 | import { isMobile } from 'react-device-detect'
5 | import { useLocation } from 'react-router-dom'
6 | import { useChainId } from 'src/hooks'
7 | import { ChainId } from '@pangolindex/sdk'
8 |
9 | const Alert = styled.div<{ isActive: any }>`
10 | width: 100%;
11 | padding: 6px 6px;
12 | background-color: ${({ theme }) => theme.blue1};
13 | color: white;
14 | font-size: 11px;
15 | justify-content: space-between;
16 | align-items: center;
17 | display: ${({ isActive }) => (isActive ? 'flex' : 'none')};
18 | `
19 |
20 | export const StyledClose = styled(X)`
21 | :hover {
22 | cursor: pointer;
23 | }
24 | `
25 | const message = `The wHBAR rewards have already been replenished, check the "Your farms" tab, stake some liquidity in the pool
26 | and click remove, this way your liquidity will be removed from the farm with your wHBAR rewards.`
27 |
28 | export default function HederaPoolWarning() {
29 | const chainId = useChainId()
30 | const { pathname } = useLocation()
31 |
32 | const [isActive, setIsActive] = useState(chainId === ChainId.HEDERA_MAINNET && pathname === '/pool/standard')
33 |
34 | useEffect(() => {
35 | if (chainId === ChainId.HEDERA_MAINNET && pathname === '/pool/standard') {
36 | setIsActive(true)
37 | }
38 | if (isActive && pathname !== '/pool/standard') {
39 | setIsActive(false)
40 | }
41 | // eslint-disable-next-line react-hooks/exhaustive-deps
42 | }, [setIsActive, pathname, chainId])
43 |
44 | const onClose = useCallback(() => setIsActive(false), [setIsActive])
45 |
46 | return isMobile ? (
47 |
48 |
52 |
53 |
54 | ) : (
55 |
56 |
60 |
61 |
62 | )
63 | }
64 |
--------------------------------------------------------------------------------
/src/components/Header/URLWarning.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { AlertTriangle, X } from 'react-feather'
4 | import { useURLWarningToggle, useURLWarningVisible } from '../../state/user/hooks'
5 | import { isMobile } from 'react-device-detect'
6 | import { useTranslation } from '@honeycomb-finance/shared'
7 |
8 | const PhishAlert = styled.div<{ isActive: any }>`
9 | width: 100%;
10 | padding: 6px 6px;
11 | background-color: ${({ theme }) => theme.blue1};
12 | color: white;
13 | font-size: 11px;
14 | justify-content: space-between;
15 | align-items: center;
16 | display: ${({ isActive }) => (isActive ? 'flex' : 'none')};
17 | `
18 |
19 | export const StyledClose = styled(X)`
20 | :hover {
21 | cursor: pointer;
22 | }
23 | `
24 |
25 | export default function URLWarning() {
26 | const toggleURLWarning = useURLWarningToggle()
27 | const showURLWarning = useURLWarningVisible()
28 | const { t } = useTranslation()
29 |
30 | return isMobile ? (
31 |
32 |
33 |
{t('header.makeSureURLWarning')}
34 |
app.pangolin.exchange
35 |
36 |
37 |
38 | ) : window.location.hostname === 'app.pangolin.exchange' ? (
39 |
40 |
41 |
{t('header.alwaysMakeSureWarning')}
42 |
app.pangolin.exchange
-
43 | {t('header.bookmarkIt')}
44 |
45 |
46 |
47 | ) : null
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/Icons/C14.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface Props {
4 | size: number
5 | fillColor: string
6 | }
7 | const C14: React.FC = props => {
8 | const { size } = props
9 | return (
10 |
38 | )
39 | }
40 |
41 | export default C14
42 |
--------------------------------------------------------------------------------
/src/components/Icons/CoinbasePay.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface Props {
4 | size: number
5 | fillColor: string
6 | }
7 | const CoinbasePay: React.FC = props => {
8 | const { size } = props
9 | return (
10 |
17 | )
18 | }
19 |
20 | export default CoinbasePay
21 |
--------------------------------------------------------------------------------
/src/components/Icons/Dashboard.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface Props {
4 | size: number
5 | fillColor: string
6 | }
7 | const Dashboard: React.FC = props => {
8 | const { size, fillColor } = props
9 | return (
10 |
44 | )
45 | }
46 |
47 | export default Dashboard
48 |
--------------------------------------------------------------------------------
/src/components/Icons/MoonPay.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface Props {
4 | size: number
5 | fillColor: string
6 | }
7 | const MoonPay: React.FC = props => {
8 | const { size } = props
9 | return (
10 |
23 | )
24 | }
25 |
26 | export default MoonPay
27 |
--------------------------------------------------------------------------------
/src/components/Icons/Swap.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface Props {
4 | size: number
5 | fillColor: string
6 | }
7 | const Swap: React.FC = props => {
8 | const { size, fillColor } = props
9 | return (
10 |
17 | )
18 | }
19 |
20 | export default Swap
21 |
--------------------------------------------------------------------------------
/src/components/Icons/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Dashboard } from './Dashboard'
2 | export { default as Swap } from './Swap'
3 | export { default as Stake } from './Stake'
4 | export { default as Buy } from './Buy'
5 | export { default as Pool } from './Pool'
6 | export { default as Vote } from './Vote'
7 | export { default as Logo } from './Logo'
8 | export { default as Bridge } from './Bridge'
9 | export { default as CoinbasePay } from './CoinbasePay'
10 | export { default as MoonPay } from './MoonPay'
11 | export { default as C14 } from './C14'
12 |
--------------------------------------------------------------------------------
/src/components/Identicon/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react'
2 | import styled from 'styled-components'
3 | import Jazzicon from '@metamask/jazzicon'
4 | import { useActiveWeb3React } from '@honeycomb-finance/shared'
5 |
6 | const StyledIdenticonContainer = styled.div`
7 | height: 100%;
8 | display: flex;
9 | align-items: center;
10 | justify-content: center;
11 | `
12 |
13 | export default function Identicon() {
14 | const ref = useRef()
15 |
16 | const { account } = useActiveWeb3React()
17 |
18 | useEffect(() => {
19 | if (account && ref.current) {
20 | ref.current.innerHTML = ''
21 | ref.current.appendChild(Jazzicon(16, parseInt(account.slice(2, 10), 16)))
22 | }
23 | }, [account])
24 |
25 | // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
26 | return
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/Loader/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import styled, { keyframes } from 'styled-components'
4 |
5 | const rotate = keyframes`
6 | from {
7 | transform: rotate(0deg);
8 | }
9 | to {
10 | transform: rotate(360deg);
11 | }
12 | `
13 |
14 | const StyledSVG = styled.svg<{ size: string; stroke?: string }>`
15 | animation: 2s ${rotate} linear infinite;
16 | height: ${({ size }) => size};
17 | width: ${({ size }) => size};
18 | path {
19 | stroke: ${({ stroke, theme }) => stroke ?? theme.primary1};
20 | }
21 | `
22 |
23 | /**
24 | * Takes in custom size and stroke for circle color, default to primary color as fill,
25 | * need ...rest for layered styles on top
26 | */
27 | export default function Loader({
28 | size = '16px',
29 | stroke,
30 | ...rest
31 | }: {
32 | size?: string
33 | stroke?: string
34 | [k: string]: any
35 | }) {
36 | return (
37 |
38 |
44 |
45 | )
46 | }
47 |
--------------------------------------------------------------------------------
/src/components/MigrationCard/styleds.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Button } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const Panel = styled(Box)`
5 | background-color: ${({ theme }) => theme.bg2};
6 | position: relative;
7 | width: 100%;
8 | height: 100%;
9 | display: flex;
10 | flex-direction: column;
11 | justify-content: flex-start;
12 | padding: 20px;
13 | border-radius: 10px;
14 | `
15 |
16 | export const OptionButton = styled.div<{ active?: boolean; disabled?: boolean }>`
17 | font-weight: 500;
18 | width: fit-content;
19 | white-space: nowrap;
20 | padding: 6px;
21 | border-radius: 6px;
22 | border: 1px solid ${({ theme }) => theme.bg4};
23 | background-color: ${({ active, theme }) => (active ? theme.yellow2 : theme.bg7)};
24 | color: ${({ theme }) => theme.text1};
25 |
26 | :hover {
27 | cursor: ${({ disabled }) => !disabled && 'pointer'};
28 | }
29 | `
30 |
31 | export const OptionsWrapper = styled.div`
32 | display: grid;
33 | grid-template-columns: auto auto auto auto;
34 | grid-gap: 10px;
35 | `
36 |
37 | export const Divider = styled(Box)`
38 | height: 1px;
39 | background-color: ${({ theme }) => theme.bg7};
40 | margin: 10px 0px 10px 0px;
41 | width: 100%;
42 | `
43 |
44 | export const MigrateButton = styled(Button)`
45 | width: fit-content;
46 | ${({ theme }) => theme.mediaWidth.upToSmall`
47 | width: 48%;
48 | `};
49 | `
50 |
51 | export const InnerWrapper = styled(Box)`
52 | display: inline-flex;
53 | justify-content: space-between;
54 | align-items: center;
55 | border: 1px solid #2c2d33;
56 | border-radius: 4px;
57 | padding: 10px;
58 | margin-top: 10px;
59 | `
60 |
--------------------------------------------------------------------------------
/src/components/MigrationModal/ChoosePool/PairData.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Pair } from '@pangolindex/sdk'
3 | import { PairBox } from './styleds'
4 | import { MinichefStakingInfo } from '@honeycomb-finance/pools'
5 | import { useTranslation } from '@honeycomb-finance/shared'
6 | import { Text, Box, DoubleCurrencyLogo, Checkbox } from '@honeycomb-finance/core'
7 | import { useGetPairDataFromPair } from '../../../state/stake/hooks'
8 |
9 | export interface PairDataProps {
10 | pair: Pair
11 | stakingData: MinichefStakingInfo | undefined
12 | selected: boolean
13 | address: string
14 | toggleIndividualSelect: (address: string) => void
15 | }
16 |
17 | const PairData = ({ pair, selected, address, toggleIndividualSelect }: PairDataProps) => {
18 | const { currency0, currency1 } = useGetPairDataFromPair(pair)
19 |
20 | const { t } = useTranslation()
21 | return (
22 | {
24 | toggleIndividualSelect(address)
25 | }}
26 | >
27 |
28 | toggleIndividualSelect(address)} />
29 |
30 |
31 |
32 |
33 | {currency0.symbol}-{currency1.symbol} {t('migratePage.pool')}
34 |
35 |
36 |
37 | )
38 | }
39 |
40 | export default PairData
41 |
--------------------------------------------------------------------------------
/src/components/MigrationModal/ChoosePool/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Wrapper } from './styleds'
3 | import { MinichefStakingInfo } from '@honeycomb-finance/pools'
4 | import { Text, Checkbox, Box, Button } from '@honeycomb-finance/core'
5 | import { useTranslation } from '@honeycomb-finance/shared'
6 |
7 | import { Pair } from '@pangolindex/sdk'
8 | import PairData from './PairData'
9 |
10 | export interface ChoosePoolProps {
11 | allChoosePool: { [address: string]: { pair: Pair; staking: MinichefStakingInfo } }
12 | allPool: { [address: string]: { pair: Pair; staking: MinichefStakingInfo } }
13 | v2IsLoading: boolean
14 | toggleSelectAll: (value: boolean) => void
15 | toggleIndividualSelect: (address: string) => void
16 | goNext: () => void
17 | }
18 |
19 | const ChoosePool = ({
20 | allChoosePool,
21 | allPool,
22 | v2IsLoading,
23 | toggleSelectAll,
24 | toggleIndividualSelect,
25 | goNext
26 | }: ChoosePoolProps) => {
27 | const { t } = useTranslation()
28 | return (
29 |
30 |
31 | {t('migratePage.migrationModalDescription')}
32 |
33 |
34 | {
37 | toggleSelectAll(check)
38 | }}
39 | checked={(Object.keys(allPool) || []).length === (Object.keys(allChoosePool) || []).length}
40 | />
41 |
42 |
43 |
44 | {(Object.keys(allPool) || []).map(address => {
45 | return (
46 |
54 | )
55 | })}
56 |
57 |
58 |
69 |
70 |
71 | )
72 | }
73 | export default ChoosePool
74 |
--------------------------------------------------------------------------------
/src/components/MigrationModal/ChoosePool/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Box } from '@honeycomb-finance/core'
3 |
4 | export const Wrapper = styled.div`
5 | margin: 0;
6 | width: 100%;
7 | `
8 | export const PairBox = styled(Box)`
9 | background-color: ${({ theme }) => theme.bg6};
10 | padding: 15px;
11 | display: flex;
12 | justify-content: space-between;
13 | align-items: center;
14 | border-radius: 4px;
15 | border: 1px solid ${({ theme }) => theme.text2};
16 | margin: 5px 0px 5px 0px;
17 | cursor: pointer;
18 | `
19 |
--------------------------------------------------------------------------------
/src/components/MigrationModal/Loader/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Text } from '@honeycomb-finance/core'
3 | import Circle from '../../../assets/svg/blue-loader.svg'
4 | import { CustomLightSpinner } from '../../../theme/components'
5 | import { Wrapper, ConfirmedIcon, Section } from './styleds'
6 | import { AutoColumn } from '../../Column'
7 |
8 | interface LoaderProps {
9 | loadingText?: string
10 | }
11 |
12 | const Loader = ({ loadingText }: LoaderProps) => {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 | {loadingText && (
21 |
22 |
23 | {loadingText}
24 |
25 |
26 | )}
27 |
28 |
29 | )
30 | }
31 |
32 | export default Loader
33 |
--------------------------------------------------------------------------------
/src/components/MigrationModal/Loader/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { ColumnCenter, AutoColumn } from '../../Column'
3 |
4 | export const Wrapper = styled.div`
5 | margin: 0;
6 | width: 100%;
7 | `
8 | export const ConfirmedIcon = styled(ColumnCenter)`
9 | padding: 60px 0;
10 | `
11 |
12 | export const Section = styled(AutoColumn)`
13 | padding: 24px;
14 | `
15 |
--------------------------------------------------------------------------------
/src/components/MigrationModal/PoolInfo/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Box, TextInput } from '@honeycomb-finance/core'
3 |
4 | export const InfoWrapper = styled.div`
5 | margin: 0;
6 | width: 100%;
7 | `
8 |
9 | export const ContentBox = styled(Box)`
10 | background-color: ${({ theme }) => theme.bg6};
11 | padding: 15px;
12 | border-radius: 4px;
13 | `
14 |
15 | export const DataBox = styled(Box)`
16 | align-items: center;
17 | justify-content: space-between;
18 | display: flex;
19 | margin: 5px 0px 5px 0px;
20 | `
21 |
22 | export const TextBox = styled(TextInput)`
23 | background-color: ${({ theme }) => theme.bg6};
24 | padding: 15px;
25 | align-items: center;
26 | border-radius: 4px;
27 | `
28 |
29 | export const StyledBalanceMax = styled.button`
30 | height: 28px;
31 | background-color: ${({ theme }) => theme.bg2};
32 | border: 1px solid ${({ theme }) => theme.bg2};
33 | border-radius: 0.5rem;
34 | font-size: 0.875rem;
35 |
36 | font-weight: 500;
37 | cursor: pointer;
38 | margin-right: 0.5rem;
39 | color: ${({ theme }) => theme.text2};
40 | :hover {
41 | border: 1px solid ${({ theme }) => theme.primary1};
42 | }
43 | :focus {
44 | border: 1px solid ${({ theme }) => theme.primary1};
45 | outline: none;
46 | }
47 |
48 | ${({ theme }) => theme.mediaWidth.upToExtraSmall`
49 | margin-right: 0.5rem;
50 | `};
51 | `
52 |
--------------------------------------------------------------------------------
/src/components/MigrationModal/Stake/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | export const Wrapper = styled.div`
4 | margin: 0;
5 | width: 100%;
6 | `
7 |
--------------------------------------------------------------------------------
/src/components/MigrationModal/StepView/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { ColumnCenter, AutoColumn } from '../../Column'
3 |
4 | export const Wrapper = styled.div`
5 | margin: 0;
6 | width: 100%;
7 | `
8 | export const ConfirmedIcon = styled(ColumnCenter)`
9 | padding: 60px 0;
10 | `
11 |
12 | export const Section = styled(AutoColumn)`
13 | padding: 24px;
14 | `
15 |
--------------------------------------------------------------------------------
/src/components/MigrationModal/Unstake/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | export const Wrapper = styled.div`
4 | margin: 0;
5 | width: 100%;
6 | `
7 |
--------------------------------------------------------------------------------
/src/components/MigrationModal/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { ApplicationModal } from '../../state/application/actions'
3 | import { useModalOpen, useMigrationModalToggle } from '../../state/application/hooks'
4 | import { Wrapper } from './styleds'
5 | import StepView from './StepView'
6 | import { Pair } from '@pangolindex/sdk'
7 | import { MinichefStakingInfo } from '@honeycomb-finance/pools'
8 | import { Modal } from '@honeycomb-finance/core'
9 | import { ThemeContext } from 'styled-components'
10 |
11 | export interface MigrationModalProps {
12 | selectedPool?: { [address: string]: { pair: Pair; staking: MinichefStakingInfo } }
13 | version: number
14 | }
15 |
16 | const MigrationModal = ({ selectedPool, version }: MigrationModalProps) => {
17 | const migrationModalOpen = useModalOpen(ApplicationModal.MIGRATION)
18 | const toggleMigrationModal = useMigrationModalToggle()
19 | const theme = useContext(ThemeContext)
20 |
21 | return (
22 |
23 |
24 |
25 |
26 |
27 | )
28 | }
29 | export default MigrationModal
30 |
--------------------------------------------------------------------------------
/src/components/MigrationModal/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | export const Wrapper = styled.div`
4 | ${({ theme }) => theme.flexColumnNoWrap}
5 | margin: 0;
6 | padding: 30px;
7 | width: 100%;
8 | `
9 |
--------------------------------------------------------------------------------
/src/components/Popups/TransactionPopup.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { AlertCircle, CheckCircle } from 'react-feather'
3 | import styled, { ThemeContext } from 'styled-components'
4 | import { useChainId } from '../../hooks'
5 | import { TYPE } from '../../theme'
6 | import { ExternalLink } from '../../theme/components'
7 | import { AutoColumn } from '../Column'
8 | import { AutoRow } from '../Row'
9 | import { useTranslation, getEtherscanLink } from '@honeycomb-finance/shared'
10 |
11 | const RowNoFlex = styled(AutoRow)`
12 | flex-wrap: nowrap;
13 | `
14 |
15 | export default function TransactionPopup({
16 | hash,
17 | success,
18 | summary
19 | }: {
20 | hash: string
21 | success?: boolean
22 | summary?: string
23 | }) {
24 | const chainId = useChainId()
25 |
26 | const { t } = useTranslation()
27 | const theme = useContext(ThemeContext)
28 |
29 | return (
30 |
31 |
34 |
35 |
36 | {summary ?? t('popups.hash') + hash.slice(0, 8) + '...' + hash.slice(58, 65)}
37 |
38 | {chainId && (
39 | {t('popups.viewExplorer')}
40 | )}
41 |
42 |
43 | )
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/Popups/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { useActivePopups } from '../../state/application/hooks'
4 | import { AutoColumn } from '../Column'
5 | import PopupItem from './PopupItem'
6 | import { useURLWarningVisible } from '../../state/user/hooks'
7 | import { Portal } from 'react-portal'
8 |
9 | const MobilePopupWrapper = styled.div<{ height: string | number }>`
10 | position: relative;
11 | max-width: 100%;
12 | height: ${({ height }) => height};
13 | margin: ${({ height }) => (height ? '0 auto;' : 0)};
14 | margin-bottom: ${({ height }) => (height ? '20px' : 0)};
15 |
16 | display: none;
17 | ${({ theme }) => theme.mediaWidth.upToSmall`
18 | display: block;
19 | `};
20 | `
21 |
22 | const MobilePopupInner = styled.div`
23 | height: 99%;
24 | overflow-x: auto;
25 | overflow-y: hidden;
26 | display: flex;
27 | flex-direction: row;
28 | -webkit-overflow-scrolling: touch;
29 | ::-webkit-scrollbar {
30 | display: none;
31 | }
32 | `
33 |
34 | const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean }>`
35 | position: fixed;
36 | top: ${({ extraPadding }) => (extraPadding ? '108px' : '88px')};
37 | right: 1rem;
38 | max-width: 355px !important;
39 | width: 100%;
40 | z-index: 1000;
41 |
42 | ${({ theme }) => theme.mediaWidth.upToSmall`
43 | display: none;
44 | `};
45 | `
46 |
47 | export default function Popups() {
48 | // get all popups
49 | const activePopups = useActivePopups()
50 |
51 | const urlWarningActive = useURLWarningVisible()
52 |
53 | return (
54 |
55 |
56 | {activePopups.map((item: any) => (
57 |
58 | ))}
59 |
60 | 0 ? 'fit-content' : 0}>
61 |
62 | {activePopups // reverse so new items up front
63 | .slice(0)
64 | .reverse()
65 | .map((item: any) => (
66 |
67 | ))}
68 |
69 |
70 |
71 | )
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/Row/index.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Box } from 'rebass/styled-components'
3 |
4 | const Row = styled(Box)<{ align?: string; padding?: string; border?: string; borderRadius?: string }>`
5 | width: 100%;
6 | display: flex;
7 | padding: 0;
8 | align-items: ${({ align }) => (align ? align : 'center')};
9 | padding: ${({ padding }) => padding};
10 | border: ${({ border }) => border};
11 | border-radius: ${({ borderRadius }) => borderRadius};
12 | `
13 |
14 | export const RowBetween = styled(Row)`
15 | justify-content: space-between;
16 | `
17 |
18 | export const AutoRow = styled(Row)<{ gap?: string; justify?: string }>`
19 | flex-wrap: wrap;
20 | margin: ${({ gap }) => gap && `-${gap}`};
21 | justify-content: ${({ justify }) => justify && justify};
22 |
23 | & > * {
24 | margin: ${({ gap }) => gap} !important;
25 | }
26 | `
27 |
28 | export const RowFixed = styled(Row)<{ gap?: string; justify?: string }>`
29 | width: fit-content;
30 | margin: ${({ gap }) => gap && `-${gap}`};
31 | `
32 |
--------------------------------------------------------------------------------
/src/components/Stat/styled.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | export const AnalyticsLink = styled.a`
4 | display: flex;
5 | align-items: center;
6 | padding: 4px;
7 | border-radius: 4px;
8 |
9 | svg {
10 | height: 16px;
11 | }
12 |
13 | path {
14 | fill: ${({ theme }) => theme.text1};
15 | }
16 |
17 | &:hover,
18 | &focus {
19 | path {
20 | fill: ${({ theme }) => theme.yellow1};
21 | }
22 | }
23 | `
24 |
--------------------------------------------------------------------------------
/src/components/StyledMenu/index.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | export const StyledMenuButton = styled.button`
4 | position: relative;
5 | height: 100%;
6 | border: none;
7 | background-color: transparent;
8 | margin: 0;
9 | padding: 0;
10 | background-color: ${({ theme }) => theme.bg2};
11 |
12 | padding: 8px 12px;
13 | border-radius: 4px;
14 |
15 | :hover,
16 | :focus {
17 | cursor: pointer;
18 | outline: none;
19 | background-color: ${({ theme }) => theme.bg4};
20 | }
21 |
22 | svg {
23 | margin-top: 2px;
24 | }
25 | `
26 |
27 | export const StyledMenu = styled.div`
28 | margin-left: 0.5rem;
29 | display: flex;
30 | justify-content: center;
31 | align-items: center;
32 | position: relative;
33 | border: none;
34 | text-align: left;
35 | `
36 |
37 | export const MenuFlyout = styled.span`
38 | min-width: 20.125rem;
39 | background-color: ${({ theme }) => theme.bg2};
40 | box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
41 | 0px 24px 32px rgba(0, 0, 0, 0.01);
42 | border-radius: 12px;
43 | display: flex;
44 | flex-direction: column;
45 | font-size: 1rem;
46 | position: absolute;
47 | top: 4rem;
48 | right: 0rem;
49 | z-index: 100;
50 | `
51 |
--------------------------------------------------------------------------------
/src/components/SwitchSubgraph/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ToggleButtons } from '@honeycomb-finance/core'
3 | import { useApplicationState } from '@honeycomb-finance/state-hooks'
4 | import { useChainId } from 'src/hooks'
5 |
6 | export default function SwitchSubgraph() {
7 | const chainId = useChainId()
8 | const { useSubgraph, setUseSubgraph } = useApplicationState()
9 |
10 | return (
11 | {
15 | setUseSubgraph((state: any) => ({
16 | ...state,
17 | [chainId]: value === 'Subgraph'
18 | }))
19 | window.location.reload()
20 | }}
21 | />
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/Web3ReactManager/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { useWeb3React } from '@web3-react/core'
3 | import styled from 'styled-components'
4 | import { NetworkContextName, useTranslation } from '@honeycomb-finance/shared'
5 | import Loader from '../Loader'
6 |
7 | const MessageWrapper = styled.div`
8 | display: flex;
9 | align-items: center;
10 | justify-content: center;
11 | height: 20rem;
12 | `
13 |
14 | const Message = styled.h2`
15 | color: ${({ theme }) => theme.secondary1};
16 | `
17 |
18 | export default function Web3ReactManager({ children }: { children: JSX.Element }) {
19 | const { t } = useTranslation()
20 | const { active } = useWeb3React()
21 | const { active: networkActive, error: networkError } = useWeb3React(NetworkContextName)
22 |
23 | // handle delayed loader state
24 | const [showLoader, setShowLoader] = useState(false)
25 | useEffect(() => {
26 | const timeout = setTimeout(() => {
27 | setShowLoader(true)
28 | }, 600)
29 |
30 | return () => {
31 | clearTimeout(timeout)
32 | }
33 | }, [])
34 |
35 | // if the account context isn't active, and there's an error on the network context, it's an irrecoverable error
36 | if (!active && networkError) {
37 | return (
38 |
39 | {t('web3ReactManager.unknownError')}
40 |
41 | )
42 | }
43 |
44 | // if neither context is active, spin
45 | if (!active && !networkActive) {
46 | return showLoader ? (
47 |
48 |
49 |
50 | ) : null
51 | }
52 |
53 | return children
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/earn/styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { AutoColumn } from '../Column'
3 |
4 | export const DataCard = styled(AutoColumn)<{ disabled?: boolean }>`
5 | background: radial-gradient(76.02% 75.41% at 1.84% 0%, #f97316 0%, #e84142 100%);
6 | border-radius: 12px;
7 | width: 100%;
8 | position: relative;
9 | overflow: hidden;
10 | `
11 |
12 | export const CardSection = styled(AutoColumn)<{ disabled?: boolean }>`
13 | padding: 1rem;
14 | z-index: 1;
15 | opacity: ${({ disabled }) => disabled && '0.4'};
16 | `
17 |
--------------------------------------------------------------------------------
/src/constants/abis/staking-rewards.ts:
--------------------------------------------------------------------------------
1 | import { Interface } from '@ethersproject/abi'
2 | import StakingRewards from '@pangolindex/governance/artifacts/contracts/StakingRewards.sol/StakingRewards.json'
3 |
4 | const STAKING_REWARDS_INTERFACE = new Interface(StakingRewards.abi)
5 |
6 | export { STAKING_REWARDS_INTERFACE }
7 |
--------------------------------------------------------------------------------
/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 | import { ChainId, ALL_CHAINS, CHAINS, TokenAmount } from '@pangolindex/sdk'
2 | import { PANGOLIN_API_BASE_URL, Tokens, useActiveWeb3React } from '@honeycomb-finance/shared'
3 | import { useQuery } from 'react-query'
4 | import axios from 'axios'
5 |
6 | export function useChainId() {
7 | const { chainId } = useActiveWeb3React()
8 | return (chainId || ChainId.AVALANCHE) as ChainId
9 | }
10 |
11 | export const useChain = (chainId: number) => {
12 | return ALL_CHAINS.filter(chain => chain.chain_id === chainId)[0]
13 | }
14 |
15 | export const usePngSymbol = () => {
16 | const chainId = useChainId()
17 | return CHAINS[chainId || ChainId.AVALANCHE].png_symbol!
18 | }
19 |
20 | export function usePNGCirculationSupply() {
21 | const chainId = useChainId()
22 | const { PNG } = Tokens
23 | const png = PNG[chainId]
24 |
25 | return useQuery(
26 | ['png-circulation-supply', png.chainId, png.address],
27 | async () => {
28 | if (!png) return undefined
29 |
30 | try {
31 | const response = await axios.get(`${PANGOLIN_API_BASE_URL}/v2/${chainId}/png/circulating-supply`, {
32 | timeout: 3 * 1000 // 3 seconds
33 | })
34 |
35 | const data = response.data
36 | return new TokenAmount(png, data)
37 | } catch (error) {
38 | return undefined
39 | }
40 | },
41 | {
42 | cacheTime: 60 * 5 * 1000 // 5 minutes
43 | }
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/src/hooks/mixpanel.tsx:
--------------------------------------------------------------------------------
1 | import { useCallback } from 'react'
2 | import {
3 | useMixpanel as useComponentMixpanel,
4 | MixPanelEvents as ComponentsMixPanelEvents
5 | } from '@honeycomb-finance/shared'
6 |
7 | export enum MixPanelEvents {}
8 |
9 | export function useMixpanel() {
10 | const { track } = useComponentMixpanel()
11 |
12 | const overrideTrack = useCallback(
13 | (event: MixPanelEvents | ComponentsMixPanelEvents, properties: { [x: string]: any }) => {
14 | track(event as any, { source: 'interface', ...properties })
15 | },
16 | [track]
17 | )
18 |
19 | return { track: overrideTrack }
20 | }
21 |
--------------------------------------------------------------------------------
/src/hooks/useCopyClipboard.ts:
--------------------------------------------------------------------------------
1 | import copy from 'copy-to-clipboard'
2 | import { useCallback, useEffect, useState } from 'react'
3 |
4 | export default function useCopyClipboard(timeout = 500): [boolean, (toCopy: string) => void] {
5 | const [isCopied, setIsCopied] = useState(false)
6 |
7 | const staticCopy = useCallback(text => {
8 | const didCopy = copy(text)
9 | setIsCopied(didCopy)
10 | }, [])
11 |
12 | useEffect(() => {
13 | if (isCopied) {
14 | const hide = setTimeout(() => {
15 | setIsCopied(false)
16 | }, timeout)
17 |
18 | return () => {
19 | clearTimeout(hide)
20 | }
21 | }
22 | return undefined
23 | }, [isCopied, setIsCopied, timeout])
24 |
25 | return [isCopied, staticCopy]
26 | }
27 |
--------------------------------------------------------------------------------
/src/hooks/useTransactionDeadline.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber } from 'ethers'
2 | import { useSelector } from '../state'
3 |
4 | // combines the current timestamp with the user setting to give the deadline that should be used for any submitted transaction
5 | // enforces a minimum deadline of 10 seconds from now
6 | export default function useTransactionDeadline(): BigNumber | undefined {
7 | const ttl = useSelector(state => state.user.userDeadline)
8 | const currentTimestampSeconds = BigNumber.from(Math.ceil(Date.now() / 1000))
9 | return ttl && ttl > 10 ? currentTimestampSeconds.add(ttl) : currentTimestampSeconds.add(10)
10 | }
11 |
--------------------------------------------------------------------------------
/src/hooks/useWindowSize.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | const isClient = typeof window === 'object'
4 |
5 | function getSize() {
6 | return {
7 | width: isClient ? window.innerWidth : undefined,
8 | height: isClient ? window.innerHeight : undefined
9 | }
10 | }
11 |
12 | // https://usehooks.com/useWindowSize/
13 | export function useWindowSize() {
14 | const [windowSize, setWindowSize] = useState(getSize)
15 |
16 | useEffect(() => {
17 | function handleResize() {
18 | setWindowSize(getSize())
19 | }
20 |
21 | if (isClient) {
22 | window.addEventListener('resize', handleResize)
23 | return () => {
24 | window.removeEventListener('resize', handleResize)
25 | }
26 | }
27 | return undefined
28 | }, [])
29 |
30 | return windowSize
31 | }
32 |
--------------------------------------------------------------------------------
/src/layout/Footer/PolicyModal.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box, Modal } from '@honeycomb-finance/core'
3 | import ReactMarkdown from 'react-markdown'
4 | import { Scrollbars } from 'react-custom-scrollbars'
5 | import { CloseButton, Content, PolicyText } from './styled'
6 |
7 | interface Props {
8 | selectPolicy: string
9 | open: boolean
10 | closeModal: () => void
11 | }
12 |
13 | export default function PolicyModal({ selectPolicy, open, closeModal }: Props) {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | {selectPolicy}
23 |
24 |
25 |
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/src/layout/Footer/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react'
2 | import { Circle } from 'react-feather'
3 | import { Box, Text } from '@honeycomb-finance/core'
4 | import { ThemeContext } from 'styled-components'
5 |
6 | import { FooterFrame, Button, Policies } from './styled'
7 | import PolicyModal from './PolicyModal'
8 | import { PrivacyPolicy } from '../../constants/Policies/PrivacyPolicy'
9 | import { TermsService } from '../../constants/Policies/TermsService'
10 | import { CookiePolicy } from '../../constants/Policies/CPolicy'
11 |
12 | export default function Footer() {
13 | const [selectedPolicy, setSelectPolicy] = useState('')
14 | const theme = useContext(ThemeContext)
15 |
16 | const openModal = (policy: string) => {
17 | setSelectPolicy(policy)
18 | }
19 |
20 | const closeModal = () => {
21 | setSelectPolicy('')
22 | }
23 |
24 | return (
25 |
26 | 0} closeModal={closeModal} />
27 |
28 |
31 |
32 |
35 |
36 |
39 |
40 |
41 | Powered by
42 |
43 | Pangolin DAO
44 |
45 |
46 |
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/src/layout/Footer/styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Box, Text } from '@honeycomb-finance/core'
3 | import { CloseIcon } from 'src/theme'
4 |
5 | export const FooterFrame = styled.footer`
6 | width: 100%;
7 | height: 50px;
8 | display: grid;
9 | grid-template-columns: 75% 25%;
10 | align-items: center;
11 | background-color: ${({ theme }) => theme.color2};
12 |
13 | ${({ theme }) => theme.mediaWidth.upToMedium`
14 | grid-template-columns: 1fr;
15 | `};
16 | `
17 |
18 | export const Policies = styled.div`
19 | display: flex;
20 | align-items: center;
21 | margin-left: 10px;
22 |
23 | ${({ theme }) => theme.mediaWidth.upToMedium`
24 | justify-content: center;
25 | `};
26 | `
27 |
28 | export const Button = styled(Text)`
29 | color: ${({ theme }) => theme.text1};
30 | cursor: pointer;
31 | &:hover {
32 | text-decoration: underline;
33 | }
34 | `
35 |
36 | export const CloseButton = styled(CloseIcon)`
37 | color: ${({ theme }) => theme.text1};
38 | position: absolute;
39 | right: 9px;
40 | top: 9px;
41 | `
42 |
43 | export const Content = styled(Box)`
44 | width: 70vw;
45 | height: 70vh;
46 | padding: 30px;
47 |
48 | ${({ theme }) => theme.mediaWidth.upToSmall`
49 | width: 100%;
50 | height: 100%;
51 | `};
52 | `
53 |
54 | export const PolicyText = styled(Box)`
55 | width: 100%;
56 | color: ${({ theme }) => theme.text1};
57 | & a {
58 | color: ${({ theme }) => theme.text1};
59 | }
60 | overflow-x: hidden;
61 | `
62 |
--------------------------------------------------------------------------------
/src/layout/Header/MenuIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { IconMenu } from './styled'
3 |
4 | interface Props {
5 | active: boolean
6 | handleMobileMenu: () => void
7 | }
8 |
9 | export const MenuIcon: React.FC = ({ active, handleMobileMenu }) => {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/src/layout/Header/MobileHeader.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@honeycomb-finance/core'
2 | import React from 'react'
3 | import Logo from '../Logo'
4 | import { MenuIcon } from './MenuIcon'
5 | import { MobileHeaderFrame, MobileLogoWrapper } from './styled'
6 |
7 | interface Props {
8 | activeMobileMenu: boolean
9 | handleMobileMenu: () => void
10 | }
11 |
12 | export const MobileHeader: React.FC = ({ activeMobileMenu, handleMobileMenu }) => {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/src/layout/Logo/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box } from '@honeycomb-finance/core'
3 | import Logo from 'src/assets/svg/logoIcon.svg'
4 | import LogoDark from 'src/assets/svg/logoSloganDark.svg'
5 | import LogoLight from 'src/assets/svg/logoSloganLight.svg'
6 |
7 | import { Title, LogoWrapper } from './styled'
8 | import { useIsDarkMode } from 'src/state/user/hooks'
9 |
10 | interface LogoProps {
11 | collapsed: boolean
12 | }
13 |
14 | export default function LogoIcon({ collapsed }: LogoProps) {
15 | const isDark = useIsDarkMode()
16 | return (
17 |
18 |
19 |
20 | {!collapsed ? (
21 |
22 | ) : (
23 |
24 | )}
25 |
26 |
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/layout/Logo/styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | export const Title = styled.a`
4 | display: flex;
5 | align-items: center;
6 | pointer-events: auto;
7 | ${({ theme }) => theme.mediaWidth.upToSmall`
8 | justify-self: center;
9 | `};
10 | cursor: pointer;
11 | overflow: hidden !important;
12 | `
13 |
14 | export const LogoWrapper = styled.div<{ collapsed: boolean }>`
15 | display: flex;
16 | align-items: center;
17 | height: 50px;
18 | padding: 10px;
19 | `
20 |
--------------------------------------------------------------------------------
/src/layout/Sidebar/NavItem.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState, useCallback, useEffect } from 'react'
2 | import { ThemeContext } from 'styled-components'
3 | import { MenuItem, MenuLink, MenuName } from './styled'
4 | import { MENU_LINK } from 'src/constants'
5 |
6 | export interface LinkProps {
7 | link: MENU_LINK | string
8 | icon:
9 | | string
10 | | React.FC<{
11 | size: number
12 | fillColor: string
13 | }>
14 | //icon: any
15 | title: string
16 | id: string
17 | isActive?: boolean
18 | childrens?: Array
19 | }
20 |
21 | interface NavItemProps {
22 | item: LinkProps
23 | collapsed?: boolean
24 | onClick?: () => void
25 | }
26 |
27 | const NavItem = ({ item, collapsed, onClick }: NavItemProps) => {
28 | const [expanded, setExpand] = useState(false)
29 | const theme = useContext(ThemeContext)
30 |
31 | const onExpandChange = useCallback(() => {
32 | setExpand(expanded => !expanded)
33 | }, [setExpand])
34 |
35 | useEffect(() => {
36 | setExpand(false)
37 | }, [collapsed])
38 |
39 | const renderMenuItem = (x: LinkProps, isChildren: boolean) => {
40 | const Icon = x.icon
41 | const isHaveChildren = x?.childrens && x?.childrens?.length > 0
42 | const menuItemInnerProps = isHaveChildren ? { onClick: onExpandChange } : { to: x.link, onClick }
43 |
44 | return (
45 |
55 | )
56 | }
57 |
58 | return (
59 | <>
60 | {renderMenuItem(item, false)}
61 |
62 | {expanded &&
63 | (item?.childrens || []).map((subItem: LinkProps) => {
64 | return renderMenuItem(subItem, true)
65 | })}
66 | >
67 | )
68 | }
69 | export default NavItem
70 |
--------------------------------------------------------------------------------
/src/layout/Sidebar/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useWindowSize } from '../../hooks/useWindowSize'
3 | import SocialMedia from '../SocialMedia'
4 | import { Sider, CollapseBar, BottomBar } from './styled'
5 | import Backward from '../../assets/svg/backward.svg'
6 | import Forward from '../../assets/svg/forward.svg'
7 | import { Scrollbars } from 'react-custom-scrollbars'
8 | import Logo from '../Logo'
9 | import { MenuLinks } from './MenuLinks'
10 | interface SidebarProps {
11 | collapsed: boolean
12 | onCollapsed: (isCollapsed: boolean) => void
13 | }
14 |
15 | export default function Sidebar({ collapsed, onCollapsed }: SidebarProps) {
16 | const { height } = useWindowSize()
17 |
18 | return (
19 | {
22 | onCollapsed(false)
23 | }}
24 | onMouseLeave={() => {
25 | if (!collapsed) {
26 | onCollapsed(true)
27 | }
28 | }}
29 | >
30 |
31 |
32 |
38 |
39 |
40 |
41 |
42 | onCollapsed(!collapsed)}>
43 | {collapsed ? (
44 |
45 | ) : (
46 |
47 | )}
48 |
49 |
50 |
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/src/layout/SocialMedia/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useTranslation } from '@honeycomb-finance/shared'
3 | import { Text, Box } from '@honeycomb-finance/core'
4 | import { Wrapper, IconWrapper, Icon, Link } from './styled'
5 | import Telegram from '../../assets/svg/social/telegram.svg'
6 | import Twitter from '../../assets/svg/social/twitter.svg'
7 | import Youtube from '../../assets/svg/social/youtube.svg'
8 | import Medium from '../../assets/svg/social/medium.svg'
9 | import Github from '../../assets/svg/social/github.svg'
10 | import Discord from '../../assets/svg/social/discord.svg'
11 | import Substack from '../../assets/svg/social/substack.svg'
12 |
13 | interface SocialMediaProps {
14 | collapsed: boolean
15 | }
16 |
17 | export default function SocialMedia({ collapsed }: SocialMediaProps) {
18 | const { t } = useTranslation()
19 |
20 | const socialLinks = [
21 | {
22 | link: 'https://twitter.com/pangolindex',
23 | icon: Twitter,
24 | title: 'Twitter'
25 | },
26 | {
27 | link: 'https://t.me/pangolindexV2',
28 | icon: Telegram,
29 | title: 'Telegram'
30 | },
31 | {
32 | link: 'https://www.youtube.com/channel/UClJJTG4FRL4z3AOf-ZWXZLw',
33 | icon: Youtube,
34 | title: 'Youtube'
35 | },
36 | {
37 | link: 'https://pangolindex.medium.com/',
38 | icon: Medium,
39 | title: 'Medium'
40 | },
41 | {
42 | link: 'https://github.com/pangolindex',
43 | icon: Github,
44 | title: 'Github'
45 | },
46 | {
47 | link: 'https://discord.gg/pangolin',
48 | icon: Discord,
49 | title: 'Discord'
50 | },
51 | {
52 | link: 'https://pangolin.substack.com/',
53 | icon: Substack,
54 | title: 'Substack'
55 | }
56 | ]
57 |
58 | return (
59 |
60 | {!collapsed && (
61 |
62 |
63 | {t('header.comeAndJoinUs')}
64 |
65 |
66 | )}
67 |
68 |
69 | {socialLinks.map((x, index) => {
70 | return (
71 |
72 |
73 |
74 | )
75 | })}
76 |
77 |
78 | )
79 | }
80 |
--------------------------------------------------------------------------------
/src/layout/SocialMedia/styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { ExternalLink } from '../../theme'
3 |
4 | export const Wrapper = styled.div`
5 | background-color: ${({ theme }) => theme.bg2};
6 | width: 100%;
7 | padding: 10px;
8 | color: ${({ theme }) => theme.text2};
9 | text-align: center;
10 | background-color: ${({ theme }) => theme.bg6};
11 | margin-bottom: 10px;
12 | `
13 |
14 | export const IconWrapper = styled.div<{ collapsed: boolean }>`
15 | display: flex;
16 | justify-content: center;
17 | flex-direction: ${({ collapsed }) => (collapsed ? 'column' : 'row')};
18 | align-items: center;
19 | margin: 5px 0px 10px 0px;
20 | `
21 |
22 | export const Icon = styled.img`
23 | margin-right: 5px;
24 | margin-top: 5px;
25 |
26 | ${({ theme }) => theme.mediaWidth.upToSmall`
27 | height: 24px;
28 | `};
29 | `
30 |
31 | export const Link = styled(ExternalLink)`
32 | ${({ theme }) => theme.flexRowNoWrap}
33 |
34 | cursor: pointer;
35 | text-decoration: none;
36 | `
37 |
--------------------------------------------------------------------------------
/src/layout/styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Box } from '@honeycomb-finance/core'
3 |
4 | export const MainContent = styled.div<{ collapsed: boolean }>`
5 | &&& {
6 | min-height: 100vh;
7 | margin-left: 70px;
8 | width: calc(100% - 70px);
9 | display: flex;
10 | flex-direction: column;
11 |
12 | ${({ theme }) => theme.mediaWidth.upToSmall`
13 | margin-left: 0;
14 | width : 100%;
15 | `};
16 | }
17 | `
18 |
19 | export const AppContent = styled.div`
20 | display: flex;
21 | flex-direction: column;
22 | width: 100%;
23 | padding-top: 100px;
24 | padding: 0px 50px;
25 | height: 100%;
26 | flex: 1;
27 | overflow-y: auto;
28 | overflow-x: hidden;
29 | z-index: 10;
30 |
31 | ${({ theme }) => theme.mediaWidth.upToSmall`
32 | padding:16px;
33 | padding-bottom: 70px;
34 | `};
35 |
36 | z-index: 1;
37 | `
38 |
39 | export const Wrapper = styled(Box)`
40 | flex: 1;
41 | `
42 |
--------------------------------------------------------------------------------
/src/pages/Beta/Bridge/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Bridge } from '@honeycomb-finance/bridge'
3 | import { PageWrapper } from './styleds'
4 | // import { QuestionAnswer } from './TabulationBox'
5 |
6 | const BridgeUI = () => {
7 | return (
8 |
9 |
10 |
11 | )
12 | }
13 |
14 | export default BridgeUI
15 |
--------------------------------------------------------------------------------
/src/pages/Beta/Buy/C14/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { C14_ASSET_ID } from 'src/constants'
3 | import styled from 'styled-components'
4 |
5 | export default function C14() {
6 | const Iframe = styled.iframe`
7 | border: 0;
8 | position: absolute;
9 | left: 0;
10 | right: 0;
11 | bottom: 0;
12 |
13 | @media (max-width: 960px) {
14 | top: 80px;
15 | }
16 | `
17 |
18 | return (
19 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/pages/Beta/Buy/Moonpay/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { MOONPAY_PK } from 'src/constants'
3 | import styled from 'styled-components'
4 |
5 | export default function Moonpay() {
6 | const Iframe = styled.iframe`
7 | border: 0;
8 | position: absolute;
9 | left: 0;
10 | right: 0;
11 | bottom: 0;
12 |
13 | @media (max-width: 960px) {
14 | top: 80px;
15 | }
16 | `
17 |
18 | return (
19 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/pages/Beta/Buy/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useParams } from 'react-router-dom'
3 | import { BUY_MENU_LINK } from 'src/constants'
4 | import CoinbasePay from './CoinbasePay'
5 | import Moonpay from './Moonpay'
6 | import C14 from './C14'
7 |
8 | export type BuyProps = Record<'type', BUY_MENU_LINK>
9 |
10 | export default function BuyV2() {
11 | const params = useParams()
12 |
13 | if (params?.type === BUY_MENU_LINK.coinbasePay) {
14 | return
15 | }
16 |
17 | if (params?.type === BUY_MENU_LINK.c14) {
18 | return
19 | }
20 |
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/src/pages/Beta/Buy/styled.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 | import { ReactComponent as Wallet } from 'src/assets/svg/wallet.svg'
4 |
5 | export const ToggleWalletButton = styled(Button)`
6 | gap: 10px;
7 | display: flex;
8 | flex-direction: row;
9 | align-items: center;
10 | justify-content: center;
11 | overflow: hidden;
12 | `
13 |
14 | export const WalletIcon = styled(Wallet)<{ color?: string }>`
15 | path {
16 | fill: ${({ color, theme }) => color || theme.text1};
17 | }
18 | `
19 |
--------------------------------------------------------------------------------
/src/pages/Beta/Governance/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { GovernanceList } from '@honeycomb-finance/governance'
3 |
4 | function Governance() {
5 | return (
6 |
7 |
8 |
9 | )
10 | }
11 |
12 | export default Governance
13 |
--------------------------------------------------------------------------------
/src/pages/Beta/GovernanceDetail/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useParams } from 'react-router-dom'
3 | import { GovernanceDetail } from '@honeycomb-finance/governance'
4 |
5 | export type GovernanceDetailProps = Record<'id', 'string'>
6 |
7 | function GovernanceDetailV2() {
8 | const params = useParams()
9 | return (
10 |
11 |
12 |
13 | )
14 | }
15 |
16 | export default GovernanceDetailV2
17 |
--------------------------------------------------------------------------------
/src/pages/Beta/Policy/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactMarkdown from 'react-markdown'
3 |
4 | import { PageWrapper, PolicyText } from './styled'
5 |
6 | import { CookiePolicy } from 'src/constants/Policies/CPolicy'
7 | import { PrivacyPolicy } from 'src/constants/Policies/PrivacyPolicy'
8 | import { TermsService } from 'src/constants/Policies/TermsService'
9 |
10 | interface Props {
11 | policy: string
12 | }
13 |
14 | const policies = {
15 | cookie: CookiePolicy,
16 | privacy: PrivacyPolicy,
17 | terms: TermsService
18 | }
19 |
20 | export default function Policy({ policy }: Props) {
21 | const text = policies[policy as keyof typeof policies]
22 | return (
23 |
24 |
25 | {text}
26 |
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/pages/Beta/Policy/styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Box } from '@honeycomb-finance/core'
3 |
4 | export const PageWrapper = styled(Box)`
5 | width: 100%;
6 | `
7 |
8 | export const PolicyText = styled(Box)`
9 | margin: 10px;
10 | color: ${({ theme }) => theme.text1};
11 | & a {
12 | color: ${({ theme }) => theme.text1};
13 | }
14 | `
15 |
--------------------------------------------------------------------------------
/src/pages/Beta/Pool/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import styled from 'styled-components'
3 | import { PoolsUI } from '@honeycomb-finance/pools'
4 | import { Elixir } from '@honeycomb-finance/elixir'
5 | import { useTranslation } from '@honeycomb-finance/shared'
6 | import { useNavigate, useParams } from 'react-router-dom'
7 | import { MENU_LINK, POOL_MENU_LINK } from 'src/constants'
8 | import { useChainId } from 'src/hooks'
9 | import { shouldHideChildItem } from 'src/utils'
10 | import { AlertTriangle } from 'react-feather'
11 | export type PoolProps = Record<'type', POOL_MENU_LINK>
12 |
13 | const PhishAlert = styled.div`
14 | width: 100%;
15 | padding: 6px 6px;
16 | background-color: ${({ theme }) => theme.red1};
17 | color: white;
18 | font-size: 11px;
19 | justify-content: space-between;
20 | align-items: center;
21 | display: flex;
22 | margin-bottom: 10px;
23 | `
24 |
25 | const Alert = () => {
26 | const { t } = useTranslation()
27 |
28 | return (
29 |
30 |
31 |
{t('elixir.auditWarning')}
32 |
33 |
34 | )
35 | }
36 |
37 | const Pool = () => {
38 | const params = useParams()
39 | const chainId = useChainId()
40 | const navigate = useNavigate()
41 |
42 | useEffect(() => {
43 | if (chainId && shouldHideChildItem(chainId, MENU_LINK.pool, params?.type as POOL_MENU_LINK)) {
44 | navigate('/')
45 | }
46 | }, [chainId, params?.type])
47 |
48 | if (params?.type === POOL_MENU_LINK.standard) {
49 | return
50 | }
51 |
52 | if (params?.type === POOL_MENU_LINK.elixir) {
53 | return (
54 | <>
55 |
56 |
57 | >
58 | )
59 | }
60 |
61 | return
62 | }
63 | export default Pool
64 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/ClaimDrawer/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from 'react'
2 | import { Box, Drawer } from '@honeycomb-finance/core'
3 | import { SingleSideStakingInfo } from 'src/state/stake/hooks'
4 | import ClaimWidget from '../ClaimWidget'
5 | import RewardStakeDrawer from '../RewardStakeDrawer'
6 |
7 | type Props = {
8 | isOpen: boolean
9 | stakingInfo: SingleSideStakingInfo
10 | onClose: () => void
11 | }
12 |
13 | const ClaimDrawer: React.FC = ({ isOpen, onClose, stakingInfo }) => {
14 | const [isRewardStakeDrawerVisible, setShowRewardStakeDrawer] = useState(false)
15 |
16 | const onCloseRewardStakeDrawer = useCallback(() => {
17 | setShowRewardStakeDrawer(false)
18 | onClose()
19 | }, [onClose])
20 |
21 | return (
22 |
23 |
24 | {isOpen && (
25 | setShowRewardStakeDrawer(true)}
29 | />
30 | )}
31 |
32 |
33 |
38 |
39 | )
40 | }
41 |
42 | export default ClaimDrawer
43 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/ClaimWidget/styled.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const WidgetWrapper = styled(Box)`
5 | display: flex;
6 | flex-direction: column;
7 | flex: 1;
8 | text-align: center;
9 | padding: 20px;
10 | `
11 |
12 | export const Root = styled(Box)`
13 | display: grid;
14 | grid-template-rows: auto max-content;
15 | height: 100%;
16 | `
17 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/DetailModal/Details/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SingleSideStakingInfo } from 'src/state/stake/hooks'
3 | import { DetailsContainer } from './styled'
4 | import { Box } from '@honeycomb-finance/core'
5 | import { CoinDescription } from '@honeycomb-finance/pools'
6 | import StatDetails from '../StatDetail'
7 |
8 | type Props = {
9 | stakingInfo: SingleSideStakingInfo
10 | }
11 |
12 | const Details: React.FC = ({ stakingInfo }) => {
13 | const amountInPNG = stakingInfo?.totalStakedInPng
14 | const isStaking = stakingInfo?.stakedAmount?.greaterThan('0')
15 | const bothCurrencySame = stakingInfo?.rewardToken?.equals(stakingInfo?.totalStakedAmount?.token)
16 |
17 | return (
18 | <>
19 |
20 |
21 |
26 |
27 | {isStaking && (
28 |
29 |
34 |
35 | )}
36 |
37 |
38 |
39 | {!bothCurrencySame && (
40 |
41 |
42 |
43 | )}
44 |
45 | >
46 | )
47 | }
48 |
49 | export default Details
50 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/DetailModal/Details/styled.ts:
--------------------------------------------------------------------------------
1 | import { Box } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const DetailsContainer = styled(Box)`
5 | overflow: hidden;
6 | width: 100%;
7 | background-color: ${({ theme }) => theme.bg2};
8 | padding: 40px;
9 | flex: 1;
10 | display: flex;
11 | flex-direction: column;
12 | ${({ theme }) => theme.mediaWidth.upToSmall`
13 | border-radius: 0 10px 10px 10px;
14 | `};
15 | `
16 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/DetailModal/EarnedWidget/styled.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const Root = styled(Box)`
5 | padding: 20px;
6 | background-color: ${({ theme }) => theme.bg2};
7 | border-radius: 10px;
8 | margin-top: 25px;
9 | width: 100%;
10 | position: relative;
11 | overflow: hidden;
12 | height: 280px;
13 | ${({ theme }) => theme.mediaWidth.upToSmall`
14 | padding: 10px 20px;
15 | `};
16 | `
17 |
18 | export const StatWrapper = styled(Box)`
19 | display: grid;
20 | grid-template-columns: 50% 50%;
21 | grid-gap: 12px;
22 | margin-top: 10px;
23 | `
24 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/DetailModal/Header/styled.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const HeaderRoot = styled(Box)`
5 | display: flex;
6 | align-items: center;
7 | justify-content: space-between;
8 | padding: 20px;
9 | border-bottom: 1px solid ${({ theme }) => theme.text6};
10 |
11 | ${({ theme }) => theme.mediaWidth.upToMedium`
12 | border-bottom: none;
13 | display: flex;
14 | flex-direction: column;
15 | justify-content: flex-start;
16 | `};
17 | `
18 |
19 | export const StatsWrapper = styled(Box)<{ isStake?: boolean }>`
20 | display: grid;
21 | grid-template-columns: repeat(5, auto);
22 | grid-gap: 20px;
23 | align-items: center;
24 | ${({ theme, isStake }) => theme.mediaWidth.upToMedium`
25 | width: 100%;
26 | grid-gap: 10px;
27 | margin-top: 10px;
28 | grid-template-columns: ${isStake ? '50% 50%' : 'repeat(3, auto)'};
29 | `};
30 | `
31 |
32 | export const HeaderWrapper = styled(Box)`
33 | display: flex;
34 | align-items: center;
35 | justify-content: space-between;
36 | ${({ theme }) => theme.mediaWidth.upToMedium`
37 | width: 100%
38 | `};
39 | `
40 | export const PoolRewardsWrapper = styled(Box)`
41 | display: flex;
42 | flex-direction: column;
43 | justify-content: space-between;
44 | `
45 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/DetailModal/StakeWidget/styled.ts:
--------------------------------------------------------------------------------
1 | import { Box, Text } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const Root = styled(Box)`
5 | padding: 20px;
6 | background-color: ${({ theme }) => theme.bg2};
7 | border-radius: 10px;
8 | position: relative;
9 | display: flex;
10 | flex-direction: column;
11 | height: 400px;
12 | `
13 |
14 | export const MaxButton = styled.button`
15 | height: 28px;
16 | background-color: ${({ theme }) => theme.bg2};
17 | border: 1px solid ${({ theme }) => theme.bg2};
18 | border-radius: 0.5rem;
19 | font-size: 0.875rem;
20 | font-weight: 500;
21 | cursor: pointer;
22 | color: ${({ theme }) => theme.text2};
23 | `
24 |
25 | export const Balance = styled(Text)`
26 | font-size: 12px;
27 | display: flex;
28 | align-items: center;
29 | color: ${({ theme }) => theme.text2};
30 | `
31 |
32 | export const Buttons = styled(Box)<{ isStaked?: boolean }>`
33 | display: grid;
34 | grid-auto-flow: ${({ isStaked }) => (isStaked ? 'column' : 'row')};
35 | grid-auto-columns: minmax(0, 1fr);
36 | grid-gap: 10px;
37 | margin-top: 20px;
38 | `
39 |
40 | export const StakeWrapper = styled(Box)`
41 | width: 100%;
42 | position: relative;
43 | border-radius: 10px;
44 | background-color: ${({ theme }) => theme.bg6};
45 | padding: 10px;
46 | `
47 | export const GridContainer = styled(Box)`
48 | display: grid;
49 | grid-template-columns: minmax(auto, 50%) minmax(auto, 50%);
50 | grid-gap: 8px;
51 | `
52 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/DetailModal/StatDetail/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box, Text, Stat } from '@honeycomb-finance/core'
3 | import { useUSDCPrice } from '@honeycomb-finance/state-hooks'
4 | import { Currency, TokenAmount, CHAINS } from '@pangolindex/sdk'
5 | import { StateContainer } from './styleds'
6 | import numeral from 'numeral'
7 | import { useChainId } from 'src/hooks'
8 |
9 | interface Props {
10 | title: string
11 | amountInPNG: TokenAmount
12 | currency0: Currency | undefined
13 | }
14 |
15 | const StatDetails: React.FC = ({ title, amountInPNG, currency0 }) => {
16 | const chainId = useChainId()
17 |
18 | const usdcPriceTmp = useUSDCPrice(amountInPNG?.token)
19 | const usdcPrice = CHAINS[chainId]?.mainnet ? usdcPriceTmp : undefined
20 | const amountInUSD = CHAINS[chainId]?.mainnet
21 | ? numeral(usdcPrice?.quote(amountInPNG, chainId).toSignificant(6)).format('$0.00a')
22 | : undefined
23 |
24 | return (
25 |
26 |
27 | {title}
28 |
29 |
30 |
31 |
39 |
48 |
49 |
50 | )
51 | }
52 |
53 | export default StatDetails
54 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/DetailModal/StatDetail/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | export const StateContainer = styled.div`
4 | grid-template-columns: repeat(2, 1fr);
5 | gap: 12px;
6 | display: grid;
7 | width: 100%;
8 | align-items: center;
9 | margin-top: 12px;
10 |
11 | @media screen and (max-width: 1024px) {
12 | grid-template-columns: 1fr;
13 | align-items: stretch;
14 | }
15 |
16 | ${({ theme }) => theme.mediaWidth.upToSmall`
17 | grid-template-columns: 50% 50%;
18 | grid-gap: 8px;
19 | `};
20 | ${({ theme }) => theme.mediaWidth.upToMedium`
21 | grid-template-columns: repeat(3, 1fr);
22 | align-items: start;
23 | `};
24 | `
25 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/DetailModal/UnstakeDrawer/styled.ts:
--------------------------------------------------------------------------------
1 | import { Box } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const Wrapper = styled.div`
5 | display: flex;
6 | flex-direction: column;
7 | flex: 1;
8 | text-align: center;
9 | padding: 20px;
10 | `
11 |
12 | export const ConfirmWrapper = styled(Box)`
13 | display: flex;
14 | flex-direction: column;
15 | height: 100%;
16 | text-align: center;
17 | `
18 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/DetailModal/styled.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const DesktopWrapper = styled(Box)`
5 | width: 1080px;
6 | overflow: auto;
7 | border-radius: 10px;
8 | ${({ theme }) => theme.mediaWidth.upToMedium`
9 | display: none;
10 | `};
11 | `
12 |
13 | export const MobileWrapper = styled(Box)`
14 | width: 100%;
15 | height: 100%;
16 | display: none;
17 | ${({ theme }) => theme.mediaWidth.upToMedium`
18 | display: block;
19 | overflow: scroll;
20 | `};
21 | `
22 |
23 | export const LeftSection = styled(Box)`
24 | border-right: 2px solid ${({ theme }) => theme.text6};
25 | display: flex;
26 | flex-direction: column;
27 | `
28 |
29 | export const DetailsWrapper = styled(Box)`
30 | display: grid;
31 | grid-template-columns: minmax(auto, 65%) minmax(auto, 35%);
32 | grid-gap: 0px;
33 | `
34 |
35 | export const Tabs = styled(Box)`
36 | width: 100%;
37 | display: flex;
38 | align-items: center;
39 | `
40 |
41 | export const Tab = styled(Box)`
42 | padding: 15px 50px;
43 | font-size: 14px;
44 | color: ${({ theme }) => theme.text10};
45 | background-color: ${({ theme }) => theme.bg2};
46 |
47 | ${({ theme }) => theme.mediaWidth.upToSmall`
48 | border-radius: 10px 10px 0 0;
49 | `};
50 | `
51 |
52 | export const RightSection = styled(Box)`
53 | padding: 20px;
54 | `
55 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/PoolCard/StakeDrawer/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SingleSideStakingInfo } from 'src/state/stake/hooks'
3 | import StakeWidet from '../StakeWidget'
4 | import { useTranslation } from '@honeycomb-finance/shared'
5 | import { Drawer } from '@honeycomb-finance/core'
6 |
7 | type Props = {
8 | isOpen: boolean
9 | stakingInfo: SingleSideStakingInfo
10 | onClose: () => void
11 | }
12 |
13 | const StakeDrawer: React.FC = ({ isOpen, onClose, stakingInfo }) => {
14 | const { t } = useTranslation()
15 | return (
16 |
17 | {isOpen && }
18 |
19 | )
20 | }
21 |
22 | export default StakeDrawer
23 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/PoolCard/StakeWidget/styled.ts:
--------------------------------------------------------------------------------
1 | import { Box, TextInput } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const Root = styled(Box)`
5 | padding: 0px 20px;
6 | background-color: ${({ theme }) => theme.bg2};
7 | border-radius: 10px;
8 | position: relative;
9 | display: flex;
10 | flex-direction: column;
11 | height: 400px;
12 | `
13 |
14 | export const MaxButton = styled.button`
15 | height: 28px;
16 | background-color: ${({ theme }) => theme.bg2};
17 | border: 1px solid ${({ theme }) => theme.bg2};
18 | border-radius: 0.5rem;
19 | font-size: 0.875rem;
20 | font-weight: 500;
21 | cursor: pointer;
22 | color: ${({ theme }) => theme.text2};
23 | `
24 |
25 | export const Buttons = styled(Box)<{ isStaked?: boolean }>`
26 | display: grid;
27 | grid-auto-flow: ${({ isStaked }) => (isStaked ? 'column' : 'row')};
28 | grid-auto-columns: minmax(0, 1fr);
29 | grid-gap: 10px;
30 | margin-top: 5px;
31 | `
32 |
33 | export const StakeWrapper = styled(Box)`
34 | width: 100%;
35 | position: relative;
36 | border-radius: 10px;
37 | background-color: ${({ theme }) => theme.bg6};
38 | padding: 8px;
39 | margin-top: 10px;
40 | `
41 | export const GridContainer = styled(Box)`
42 | display: grid;
43 | grid-template-columns: minmax(auto, 50%) minmax(auto, 50%);
44 | grid-gap: 8px;
45 | `
46 | export const InputText = styled(TextInput)`
47 | padding: 5px;
48 | `
49 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/PoolCard/styleds.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Button, Text } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const Card = styled(Box)`
5 | /* width: 480px; */
6 | padding: 25px;
7 | box-sizing: border-box;
8 | border-radius: 10px;
9 | background: ${({ theme }) => theme.color2};
10 | position: relative;
11 | overflow: hidden;
12 | & img {
13 | border-radius: 100px;
14 | }
15 |
16 | ${({ theme }) => theme.mediaWidth.upToMedium`
17 | width: 100%;
18 | margin-bottom: 22px;
19 | `};
20 | `
21 |
22 | export const CardHeader = styled(Box)`
23 | display: flex;
24 | justify-content: space-between;
25 | align-items: center;
26 | padding-bottom: 25px;
27 | border-bottom: 1px solid ${({ theme }) => theme.text8};
28 | `
29 |
30 | export const Stats = styled(Box)``
31 |
32 | export const StatValue = styled(Text)`
33 | font-size: 24px;
34 | font-weight: 500;
35 |
36 | ${({ theme }) => theme.mediaWidth.upToSmall`
37 | font-size: 22px;
38 | `};
39 | `
40 |
41 | export const CardStats = styled(Box)`
42 | display: grid;
43 | grid-auto-columns: minmax(0, 1fr);
44 | grid-auto-flow: column;
45 | grid-gap: 20px;
46 | padding: 20px 0 0px;
47 | `
48 |
49 | export const TokenName = styled(Box)`
50 | font-weight: 800;
51 | font-size: 24px;
52 | line-height: 33px;
53 | color: ${({ theme }) => theme.text7};
54 | `
55 |
56 | export const StakeButton = styled(Button)`
57 | /* background-color: ${({ theme }) => theme.color5} !important; */
58 | height: 46px;
59 | border-radius: 4px !important;
60 | font-size: 14px;
61 |
62 | ${({ theme }) => theme.mediaWidth.upToSmall`
63 | font-size: 16px;
64 | `};
65 | `
66 |
67 | export const DetailButton = styled(Button)`
68 | border: solid 1px ${({ theme }) => theme.color4} !important;
69 | height: 46px;
70 | border-radius: 4px !important;
71 | font-size: 14px;
72 |
73 | ${({ theme }) => theme.mediaWidth.upToSmall`
74 | font-size: 16px;
75 | `};
76 | `
77 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/RewardStakeDrawer/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SingleSideStakingInfo } from 'src/state/stake/hooks'
3 | import StakeWidget from '../DetailModal/StakeWidget'
4 | import { Drawer } from '@honeycomb-finance/core'
5 | import { useTranslation } from '@honeycomb-finance/shared'
6 |
7 | type Props = {
8 | isOpen: boolean
9 | stakingInfo: SingleSideStakingInfo
10 | onClose: () => void
11 | }
12 |
13 | const RewardStakeDrawer: React.FC = ({ isOpen, onClose, stakingInfo }) => {
14 | const { t } = useTranslation()
15 | return (
16 |
17 | {isOpen && }
18 |
19 | )
20 | }
21 |
22 | export default RewardStakeDrawer
23 |
--------------------------------------------------------------------------------
/src/pages/Beta/Stake/styleds.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const PageWrapper = styled(Box)`
5 | width: 100%;
6 | `
7 |
8 | export const PageTitle = styled(Box)`
9 | font-weight: 500;
10 | font-size: 32px;
11 | color: ${({ theme }) => theme.text7};
12 | margin-top: 105px;
13 | margin-bottom: 92px;
14 | display: flex;
15 | justify-content: center;
16 | ${({ theme }) => theme.mediaWidth.upToSmall`
17 | font-size: 24px;
18 | text-align: center;
19 | margin-top: 30px;
20 | margin-bottom: 30px;
21 | `};
22 | `
23 |
24 | export const PoolsWrapper = styled(Box)`
25 | display: flex;
26 | justify-content: center;
27 |
28 | ${({ theme }) => theme.mediaWidth.upToMedium`
29 | flex-direction: column;
30 | `};
31 | `
32 |
33 | export const PoolCards = styled(Box)`
34 | display: grid;
35 | grid-auto-columns: minmax(0, 1fr);
36 | grid-auto-flow: column;
37 | grid-gap: 20px;
38 | min-width: 320px;
39 | margin: auto;
40 | ${({ theme }) => theme.mediaWidth.upToMedium`
41 | grid-auto-columns: minmax(0, 1fr);
42 | grid-auto-flow: row;
43 | `};
44 | `
45 |
--------------------------------------------------------------------------------
/src/pages/Beta/Swap/LimitOrderList/CancelOrder/styleds.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const CancelOrderRoot = styled(Box)`
5 | width: 100%;
6 | `
7 |
8 | export const PendingWrapper = styled(Box)`
9 | display: flex;
10 | justify-content: center;
11 | align-items: center;
12 | flex-direction: column;
13 | height: 100%;
14 | `
15 |
16 | export const Root = styled(Box)`
17 | display: grid;
18 | grid-template-rows: auto max-content;
19 | height: 100%;
20 | `
21 |
22 | export const Header = styled(Box)`
23 | padding: 0px 10px;
24 | `
25 |
26 | export const Footer = styled(Box)`
27 | padding: 0px 10px;
28 | `
29 |
--------------------------------------------------------------------------------
/src/pages/Beta/Swap/LimitOrderList/CancelOrderModal/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { Text, Box, Modal } from '@honeycomb-finance/core'
3 | import { useTranslation } from '@honeycomb-finance/shared'
4 | import { Wrapper } from './styleds'
5 | import { ThemeContext } from 'styled-components'
6 | // import { Order } from '@gelatonetwork/limit-orders-react'
7 | import { CloseIcon } from 'src/theme/components'
8 | import CancelOrder from '../CancelOrder'
9 |
10 | interface ClaimRewardModalProps {
11 | isOpen: boolean
12 | onClose: () => void
13 | order: any
14 | }
15 |
16 | const CancelOrderModal = ({ isOpen, onClose, order }: ClaimRewardModalProps) => {
17 | const theme = useContext(ThemeContext)
18 | const { t } = useTranslation()
19 |
20 | return (
21 |
22 |
23 |
24 |
25 | {t('swapPage.cancelOrder')}
26 |
27 | onClose()} color={theme.text1} />
28 |
29 | onClose()} />
30 |
31 |
32 | )
33 | }
34 | export default CancelOrderModal
35 |
--------------------------------------------------------------------------------
/src/pages/Beta/Swap/LimitOrderList/CancelOrderModal/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | export const Wrapper = styled.div`
4 | ${({ theme }) => theme.flexColumnNoWrap}
5 | margin: 0;
6 | width: 100%;
7 | align-items: center;
8 | max-width: 420px;
9 | min-width: 420px;
10 | padding: 10px;
11 | `
12 |
--------------------------------------------------------------------------------
/src/pages/Beta/Swap/LimitOrderList/LimitOrderRow.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useGelatoLimitOrderDetail, LimitOrderInfo } from '@honeycomb-finance/swap'
3 | import { Text, Box } from '@honeycomb-finance/core'
4 | import { useTranslation } from '@honeycomb-finance/shared'
5 | import { DesktopRowWrapper } from './styleds'
6 |
7 | type Props = {
8 | order: LimitOrderInfo
9 | onClick: () => void
10 | isSelected: boolean
11 | }
12 |
13 | const LimitOrderRow: React.FC = ({ order, onClick, isSelected }) => {
14 | const { t } = useTranslation()
15 | const { currency0, currency1, inputAmount, outputAmount } = useGelatoLimitOrderDetail(order)
16 |
17 | return (
18 |
19 |
20 | {t('swapPage.cancelLimitOrder', {
21 | outputCurrency: currency1?.symbol,
22 | inputCurrency: currency0?.symbol
23 | })}
24 |
25 |
26 |
27 |
28 |
29 | {inputAmount ? inputAmount.toSignificant(4) : '-'} / {outputAmount ? outputAmount.toSignificant(4) : '-'}
30 |
31 |
32 |
33 | {order?.status}
34 | {order?.pending && `(P)`}
35 |
36 |
37 |
38 |
39 | )
40 | }
41 |
42 | export default LimitOrderRow
43 |
--------------------------------------------------------------------------------
/src/pages/Beta/Swap/PairInfo/PairChart/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | export const ChartWrapper = styled.div`
4 | width: 100%;
5 | padding: 10px;
6 | border-radius: 10px;
7 | background-color: ${({ theme }) => theme.color2};
8 | position: relative;
9 | ${({ theme }) => theme.mediaWidth.upToSmall`
10 | display: none;
11 | `};
12 | `
13 |
14 | export const ChartContainer = styled.div`
15 | width: 100%;
16 | height: 100%;
17 | bottom: 0px;
18 | left: 0px;
19 | position: absolute;
20 | `
21 |
--------------------------------------------------------------------------------
/src/pages/Beta/Swap/PairInfo/PairStat/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | export const PanelWrapper = styled.div`
4 | grid-template-columns: repeat(6, 1fr);
5 | grid-template-rows: max-content;
6 | gap: 12px;
7 | display: inline-grid;
8 | width: 100%;
9 | align-items: start;
10 | border-radius: 10px;
11 | background-color: ${({ theme }) => theme.color2};
12 | @media screen and (max-width: 1024px) {
13 | grid-template-columns: 1fr;
14 | align-items: stretch;
15 | > * {
16 | grid-column: 1 / 6;
17 | }
18 |
19 | > * {
20 | &:first-child {
21 | width: 100%;
22 | }
23 | }
24 | }
25 |
26 | ${({ theme }) => theme.mediaWidth.upToSmall`
27 | display: none;
28 | `};
29 | `
30 |
31 | export const MobileStat = styled.div`
32 | border-radius: 10px;
33 | background-color: ${({ theme }) => theme.color2};
34 | display: none;
35 | padding: 10px;
36 | ${({ theme }) => theme.mediaWidth.upToSmall`
37 | display: flex;
38 | align-items: center;
39 | justify-content: space-between;
40 | `};
41 | `
42 |
--------------------------------------------------------------------------------
/src/pages/Beta/Swap/PairInfo/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PairStat from './PairStat'
3 | import PairChart from './PairChart'
4 | import { useDerivedSwapInfo } from '@honeycomb-finance/swap'
5 | import { wrappedCurrency, Tokens } from '@honeycomb-finance/shared'
6 | import { usePair } from '@honeycomb-finance/state-hooks'
7 | import { useChainId } from 'src/hooks'
8 |
9 | export enum Field {
10 | INPUT = 'INPUT',
11 | OUTPUT = 'OUTPUT'
12 | }
13 |
14 | const PairInfo = () => {
15 | const chainId = useChainId()
16 |
17 | const { currencies } = useDerivedSwapInfo()
18 |
19 | const token1 = currencies[Field.OUTPUT]
20 |
21 | const tokenB = wrappedCurrency(token1 ?? undefined, chainId)
22 | const { USDC } = Tokens
23 | // should show the price of tokenB in USD
24 | const [, tokenPair] = usePair(USDC[chainId], tokenB)
25 |
26 | return (
27 | <>
28 |
35 |
36 | >
37 | )
38 | }
39 | export default PairInfo
40 |
--------------------------------------------------------------------------------
/src/pages/Beta/Swap/SwapUI/styleds.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const PageWrapper = styled(Box)`
5 | width: 100%;
6 | padding-top: 25px;
7 | display: flex;
8 | flex-direction: column;
9 | flex: 1;
10 | ${({ theme }) => theme.mediaWidth.upToSmall`
11 | padding-top: 10px;
12 | `};
13 | `
14 |
15 | export const GridWrapper = styled(Box)`
16 | display: grid;
17 | grid-template-columns: minmax(auto, 75%) minmax(auto, 25%);
18 | grid-gap: 12px;
19 | padding: 10px 0px;
20 | ${({ theme }) => theme.mediaWidth.upToSmall`
21 | grid-template-columns: none;
22 | grid-template-rows: max-content;
23 | `};
24 | `
25 |
26 | export const TopContainer = styled(Box)`
27 | display: grid;
28 | grid-template-columns: auto minmax(auto, 400px);
29 | grid-gap: 12px;
30 |
31 | ${({ theme }) => theme.mediaWidth.upToSmall`
32 | grid-template-columns: none;
33 | grid-template-rows: max-content auto;
34 | `};
35 | `
36 |
37 | export const StatsWrapper = styled(Box)`
38 | display: grid;
39 | grid-template-rows: max-content auto;
40 | grid-gap: 12px;
41 | `
42 |
43 | export const GridContainer = styled(Box)<{ isLimitOrders: boolean }>`
44 | display: grid;
45 | grid-template-columns: ${({ isLimitOrders }) =>
46 | isLimitOrders ? `minmax(auto, 50%) minmax(auto, 25%) minmax(auto, 25%)` : `minmax(auto, 50%) minmax(auto, 50%)`};
47 | grid-gap: 12px;
48 | padding: 10px 0px;
49 |
50 | ${({ theme }) => theme.mediaWidth.upToSmall`
51 | grid-template-columns: none;
52 | grid-template-rows: max-content;
53 | `};
54 | `
55 |
--------------------------------------------------------------------------------
/src/pages/Beta/Swap/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import SwapUI from './SwapUI'
3 | import { useLibrary, useActiveWeb3React } from '@honeycomb-finance/shared'
4 | import { useChainId } from 'src/hooks'
5 | import { GelatoProvider, galetoStore } from '@honeycomb-finance/swap'
6 | import { Web3Provider } from '@ethersproject/providers'
7 | import { Provider } from 'react-redux'
8 |
9 | const Swap = () => {
10 | const chainId = useChainId()
11 | const { account } = useActiveWeb3React()
12 | const { library } = useLibrary()
13 |
14 | const ethersLibrary = library && !library?._isProvider ? new Web3Provider(library) : library
15 | return (
16 |
17 |
25 |
26 |
27 |
28 | )
29 | }
30 | export default Swap
31 |
--------------------------------------------------------------------------------
/src/pages/Dashboard/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { PageTitle, PageDescription, PageWrapper, TopContainer, StatsWrapper } from './styleds'
3 | import { useTranslation } from '@honeycomb-finance/shared'
4 | import { WatchList, Portfolio } from '@honeycomb-finance/portfolio'
5 | import { NewsWidget } from '@honeycomb-finance/core'
6 | import { CHAINS } from '@pangolindex/sdk'
7 | import { useChainId } from 'src/hooks'
8 | import { Hidden, Visible } from 'src/theme'
9 |
10 | const Dashboard = () => {
11 | const { t } = useTranslation()
12 | const chainId = useChainId()
13 |
14 | const isMainnet = CHAINS[chainId]?.mainnet
15 |
16 | return (
17 |
18 | {t('dashboardPage.dashboard')}
19 | {t('dashboardPage.greetings')}
20 |
21 |
22 | {isMainnet && (
23 |
24 |
25 |
26 |
27 | )}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | )
39 | }
40 |
41 | export default Dashboard
42 |
--------------------------------------------------------------------------------
/src/pages/Dashboard/styleds.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@honeycomb-finance/core'
2 | import styled from 'styled-components'
3 |
4 | export const PageWrapper = styled(Box)`
5 | width: 100%;
6 | height: 100%;
7 | display: flex;
8 | flex: 1;
9 | flex-direction: column;
10 | `
11 |
12 | export const TopContainer = styled(Box)<{ isMainnet: boolean }>`
13 | display: grid;
14 | grid-template-columns: ${({ isMainnet }) => (isMainnet ? `50% 50%` : `100%`)};
15 | grid-gap: 12px;
16 | margin-bottom: 22px;
17 | flex: 1;
18 | grid-template-rows: minmax(500px, 1fr);
19 | ${({ theme }) => theme.mediaWidth.upToSmall`
20 | grid-template-columns: none;
21 | grid-template-rows: max-content auto;
22 | margin-bottom: 0px;
23 | `};
24 | `
25 |
26 | export const StatsWrapper = styled(Box)`
27 | display: grid;
28 | grid-template-rows: auto auto;
29 | grid-gap: 12px;
30 | align-items: stretch;
31 | `
32 |
33 | export const PageTitle = styled.div`
34 | font-size: 28px;
35 | color: ${({ theme }) => theme.text7};
36 |
37 | ${({ theme }) => theme.mediaWidth.upToSmall`
38 | font-size: 22px;
39 | `};
40 | `
41 |
42 | export const PageDescription = styled.div`
43 | font-size: 18px;
44 | margin-bottom: 16px;
45 | color: ${({ theme }) => theme.text8};
46 |
47 | ${({ theme }) => theme.mediaWidth.upToSmall`
48 | font-size: 14px;
49 | `};
50 | `
51 |
--------------------------------------------------------------------------------
/src/pages/Migrate/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import MigrateUI from './Migrate'
3 | import { ApplicationModal } from '../../state/application/actions'
4 | import { useModalOpen } from '../../state/application/hooks'
5 |
6 | const Migrate = () => {
7 | const isModalOpen = useModalOpen(ApplicationModal.MIGRATION)
8 | const [refreshData, setRefreshData] = useState(true)
9 |
10 | // Here we have done some hacky things to refresh migration data once user complete migrate process
11 | // we are unmounting and mounting MigrateUI component so it loads all data fresh
12 | useEffect(() => {
13 | if (!isModalOpen) {
14 | setRefreshData(false)
15 | setTimeout(() => {
16 | setRefreshData(true)
17 | }, 50)
18 | }
19 | }, [isModalOpen])
20 |
21 | return refreshData ? : null
22 | }
23 |
24 | export default Migrate
25 |
--------------------------------------------------------------------------------
/src/pages/SarStake/StakeStat/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Box } from '@honeycomb-finance/core'
3 |
4 | export const Wrapper = styled(Box)`
5 | display: grid;
6 | grid-template-columns: max-content 1fr;
7 | gap: 30px;
8 | background-color: ${({ theme }) => theme.color2};
9 | border-radius: 10px;
10 | padding: 16px;
11 |
12 | @media (max-width: 920px) {
13 | grid-template-columns: 1fr 1fr;
14 | }
15 |
16 | @media (max-width: 450px) {
17 | grid-template-columns: 1fr;
18 | }
19 | `
20 |
21 | export const Title = styled(Box)`
22 | display: inline-grid;
23 | grid-template-columns: auto 1fr;
24 | gap: 8px;
25 | align-items: center;
26 | justify-items: center;
27 | width: auto;
28 | `
29 |
30 | export const StatWrapper = styled(Box)`
31 | display: grid;
32 | grid-template-columns: repeat(auto-fit, minmax(0, max-content));
33 | gap: 20px;
34 | justify-content: end;
35 | align-items: center;
36 | flex-wrap: wrap;
37 |
38 | @media (max-width: 1200px) {
39 | grid-template-columns: 1fr 1fr;
40 | grid-template-rows: 1fr 1fr;
41 | }
42 | `
43 |
--------------------------------------------------------------------------------
/src/pages/SarStake/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Box } from '@honeycomb-finance/core'
3 | import { CloseIcon } from 'src/theme'
4 |
5 | export const PageWrapper = styled(Box)`
6 | width: 100%;
7 | padding-top: 25px;
8 | display: grid;
9 | flex-grow: 1;
10 | grid-gap: 16px;
11 | grid-template-columns: 75% 25%;
12 | grid-template-rows: max-content 1fr;
13 | grid-template-areas:
14 | 'details stake'
15 | 'images stake';
16 |
17 | ${({ theme }) => theme.mediaWidth.upToLarge`
18 | grid-template-columns: 65% 35%;
19 | `}
20 |
21 | ${({ theme }) => theme.mediaWidth.upToMedium`
22 | grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
23 | `}
24 |
25 | ${({ theme }) => theme.mediaWidth.upToSmall`
26 | grid-template-columns: none;
27 | grid-template-areas:
28 | 'details'
29 | 'stake'
30 | 'images';
31 | `};
32 | `
33 | export const CloseButton = styled(CloseIcon)`
34 | color: ${({ theme }) => theme.text1};
35 | position: absolute;
36 | right: 9px;
37 | top: 9px;
38 | `
39 |
40 | export const StyledSVG = styled(Box)`
41 | svg {
42 | width: 100%;
43 | height: 100%;
44 | }
45 |
46 | height: 400px;
47 | `
48 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@metamask/jazzicon' {
2 | export default function(diameter: number, seed: number): HTMLElement
3 | }
4 |
5 | interface Window {
6 | WalletLinkProvider?: any
7 | walletLinkExtension?: any
8 | xfi?: any
9 | bitkeep?: any
10 | isBitKeep?: true
11 | ethereum?: {
12 | isCoinbaseWallet?: boolean
13 | isMetaMask?: true
14 | isXDEFI?: true
15 | isRabby?: true
16 | isTalisman?: true
17 | on?: (...args: any[]) => void
18 | removeListener?: (...args: any[]) => void
19 | request: (...args: any[]) => Promise
20 | getBlock?: (block) => Promise
21 | getTransactionReceipt?: (hash) => Promise
22 | getBlockNumber?: () => Promise
23 | execute?: (method, params) => Promise
24 | }
25 | web3?: {}
26 | avalanche?: {
27 | isAvalanche?: boolean
28 | once(eventName: string | symbol, listener: (...args: any[]) => void): this
29 | on(eventName: string | symbol, listener: (...args: any[]) => void): this
30 | off(eventName: string | symbol, listener: (...args: any[]) => void): this
31 | addListener(eventName: string | symbol, listener: (...args: any[]) => void): this
32 | removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this
33 | removeAllListeners(event?: string | symbol): this
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/setupProxy.js:
--------------------------------------------------------------------------------
1 | // App is an express application, we can add an express middleware that will set headers for manifest.json request
2 | // https://create-react-app.dev/docs/proxying-api-requests-in-development/#configuring-the-proxy-manually
3 |
4 | module.exports = function(app) {
5 | app.use('/manifest.json', function(req, res, next) {
6 | res.set({
7 | 'Access-Control-Allow-Origin': '*',
8 | 'Access-Control-Allow-Methods': 'GET',
9 | 'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization'
10 | })
11 |
12 | next()
13 | })
14 | }
15 |
--------------------------------------------------------------------------------
/src/state/application/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 |
3 | export type PopupContent = {
4 | txn: {
5 | hash: string
6 | success: boolean
7 | summary?: string
8 | }
9 | }
10 |
11 | export enum ApplicationModal {
12 | WALLET,
13 | CLAIM_POPUP,
14 | DELEGATE,
15 | VOTE,
16 | LANGUAGE,
17 | MIGRATION,
18 | FARM,
19 | SINGLE_SIDE_STAKE_DETAIL,
20 | ACCOUNT_DETAIL
21 | }
22 |
23 | export const updateBlockNumber = createAction<{ chainId: number; blockNumber: number }>('application/updateBlockNumber')
24 | export const setOpenModal = createAction('application/setOpenModal')
25 |
--------------------------------------------------------------------------------
/src/state/application/hooks.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useMemo } from 'react'
2 | import { useDispatch } from 'src/state'
3 | import { useActiveWeb3React } from '@honeycomb-finance/shared'
4 | import { useActivePopups as useActiveComponentsPopup } from '@honeycomb-finance/state-hooks'
5 | import { AppState, useSelector } from '../index'
6 | import { ApplicationModal, setOpenModal } from './actions'
7 |
8 | export function useBlockNumber(): number | undefined {
9 | const { chainId } = useActiveWeb3React()
10 | return useSelector((state: AppState) => state.application.blockNumber[chainId ?? -1])
11 | }
12 |
13 | export function useModalOpen(modal: ApplicationModal): boolean {
14 | const openModal = useSelector((state: AppState) => state.application.openModal)
15 | return openModal === modal
16 | }
17 |
18 | export function useToggleModal(modal: ApplicationModal): () => void {
19 | const open = useModalOpen(modal)
20 | const dispatch = useDispatch()
21 | return useCallback(() => dispatch(setOpenModal(open ? null : modal)), [dispatch, modal, open])
22 | }
23 |
24 | export function useMigrationModalToggle(): () => void {
25 | return useToggleModal(ApplicationModal.MIGRATION)
26 | }
27 |
28 | export function useSingleSideStakingDetailnModalToggle(): () => void {
29 | return useToggleModal(ApplicationModal.SINGLE_SIDE_STAKE_DETAIL)
30 | }
31 |
32 | export function useToggleDelegateModal(): () => void {
33 | return useToggleModal(ApplicationModal.DELEGATE)
34 | }
35 |
36 | export function useAccountDetailToggle(): () => void {
37 | return useToggleModal(ApplicationModal.ACCOUNT_DETAIL)
38 | }
39 |
40 | // get the list of active popups
41 | export function useActivePopups() {
42 | const popups = useActiveComponentsPopup()
43 | return useMemo(() => popups.filter((item: any) => item.show), [popups])
44 | }
45 |
--------------------------------------------------------------------------------
/src/state/application/reducer.ts:
--------------------------------------------------------------------------------
1 | import { createReducer } from '@reduxjs/toolkit'
2 | import { updateBlockNumber, ApplicationModal, setOpenModal } from './actions'
3 |
4 | export interface ApplicationState {
5 | readonly blockNumber: { readonly [chainId: number]: number }
6 | readonly openModal: ApplicationModal | null
7 | }
8 |
9 | const initialState: ApplicationState = {
10 | blockNumber: {},
11 | openModal: null
12 | }
13 |
14 | export default createReducer(initialState, builder =>
15 | builder
16 | .addCase(updateBlockNumber, (state, action) => {
17 | const { chainId, blockNumber } = action.payload
18 | if (typeof state.blockNumber[chainId] !== 'number') {
19 | state.blockNumber[chainId] = blockNumber
20 | } else {
21 | state.blockNumber[chainId] = Math.max(blockNumber, state.blockNumber[chainId])
22 | }
23 | })
24 | .addCase(setOpenModal, (state, action) => {
25 | state.openModal = action.payload
26 | })
27 | )
28 |
--------------------------------------------------------------------------------
/src/state/global/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 |
3 | // fired once when the app reloads but before the app renders
4 | // allows any updates to be applied to store data loaded from localStorage
5 | export const updateVersion = createAction('global/updateVersion')
6 |
--------------------------------------------------------------------------------
/src/state/index.ts:
--------------------------------------------------------------------------------
1 | import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
2 | import { save, load } from 'redux-localstorage-simple'
3 | import application from './application/reducer'
4 | import { updateVersion } from './global/actions'
5 | import user from './user/reducer'
6 | import transactions from './transactions/reducer'
7 | import multicall from './multicall/reducer'
8 | import watchlists from './watchlists/reducer'
9 | import token from './token/reducer'
10 | import pair from './pair/reducer'
11 | import { createDispatchHook, createSelectorHook, createStoreHook } from 'react-redux'
12 | import React from 'react'
13 |
14 | const PERSISTED_KEYS: string[] = ['user', 'transactions', 'lists', 'watchlists']
15 |
16 | const store = configureStore({
17 | reducer: {
18 | application,
19 | user,
20 | transactions,
21 | multicall,
22 | watchlists,
23 | token,
24 | pair
25 | },
26 | middleware: [...getDefaultMiddleware({ thunk: false }), save({ states: PERSISTED_KEYS })],
27 | preloadedState: load({ states: PERSISTED_KEYS })
28 | })
29 |
30 | store.dispatch(updateVersion())
31 |
32 | export default store
33 |
34 | export type AppState = ReturnType
35 | export type AppDispatch = typeof store.dispatch
36 |
37 | export const InterfaceContext = React.createContext(null as any)
38 |
39 | // Export your custom hooks if you wish to use them in other files.
40 | export const useStore = createStoreHook(InterfaceContext)
41 | export const useDispatch = createDispatchHook(InterfaceContext)
42 | export const useSelector = createSelectorHook(InterfaceContext)
43 |
--------------------------------------------------------------------------------
/src/state/migrate/hooks.ts:
--------------------------------------------------------------------------------
1 | import { useMemo, useState, useEffect } from 'react'
2 | import { Pair } from '@pangolindex/sdk'
3 | import { useGetStakingDataWithAPR } from '../../state/stake/hooks'
4 | import { useGetUserLP, useMinichefPools, MinichefStakingInfo } from '@honeycomb-finance/pools'
5 |
6 | export function useGetMigrationData(version: number) {
7 | const { v2IsLoading, allV2PairsWithLiquidity, allPairs } = useGetUserLP()
8 |
9 | const [allPool, setAllPool] = useState({} as { [address: string]: { pair: Pair; staking: MinichefStakingInfo } })
10 |
11 | const stakingInfos = useGetStakingDataWithAPR(Number(version))
12 |
13 | const poolMap = useMinichefPools()
14 | /* eslint-disable prefer-const */
15 | useEffect(() => {
16 | let pairs = {} as { [address: string]: { pair: Pair; staking: MinichefStakingInfo } }
17 |
18 | for (const stakingInfo of stakingInfos) {
19 | let pairAddress = stakingInfo?.stakedAmount?.token?.address
20 | let stakingData = stakingInfo
21 |
22 | let pair = (allPairs as Pair[]).find(
23 | data => data?.liquidityToken?.address === stakingData?.stakedAmount?.token?.address
24 | ) as Pair
25 |
26 | if (stakingData?.stakedAmount.greaterThan('0') && poolMap.hasOwnProperty(pairAddress)) {
27 | pairs[pairAddress] = {
28 | pair: pair,
29 | staking: stakingData
30 | }
31 | }
32 | }
33 |
34 | setAllPool(pairs)
35 |
36 | // eslint-disable-next-line react-hooks/exhaustive-deps
37 | }, [(stakingInfos || []).length])
38 |
39 | return useMemo(
40 | () => ({ allPool, v2IsLoading: v2IsLoading || stakingInfos.length === 0, allV2PairsWithLiquidity, allPairs }),
41 | [allPool, v2IsLoading, stakingInfos, allV2PairsWithLiquidity, allPairs]
42 | )
43 | }
44 |
--------------------------------------------------------------------------------
/src/state/multicall/actions.test.ts:
--------------------------------------------------------------------------------
1 | import { parseCallKey, toCallKey } from './actions'
2 |
3 | describe('actions', () => {
4 | describe('#parseCallKey', () => {
5 | it('does not throw for invalid address', () => {
6 | expect(parseCallKey('0x-0x')).toEqual({ address: '0x', callData: '0x' })
7 | })
8 | it('does not throw for invalid calldata', () => {
9 | expect(parseCallKey('0x6b175474e89094c44da98b954eedeac495271d0f-abc')).toEqual({
10 | address: '0x6b175474e89094c44da98b954eedeac495271d0f',
11 | callData: 'abc'
12 | })
13 | })
14 | it('throws for invalid format', () => {
15 | expect(() => parseCallKey('abc')).toThrow('Invalid call key: abc')
16 | })
17 | it('throws for uppercase calldata', () => {
18 | expect(parseCallKey('0x6b175474e89094c44da98b954eedeac495271d0f-0xabcD')).toEqual({
19 | address: '0x6b175474e89094c44da98b954eedeac495271d0f',
20 | callData: '0xabcD'
21 | })
22 | })
23 | it('parses pieces into address', () => {
24 | expect(parseCallKey('0x6b175474e89094c44da98b954eedeac495271d0f-0xabcd')).toEqual({
25 | address: '0x6b175474e89094c44da98b954eedeac495271d0f',
26 | callData: '0xabcd'
27 | })
28 | })
29 | })
30 |
31 | describe('#toCallKey', () => {
32 | it('throws for invalid address', () => {
33 | expect(() => toCallKey({ callData: '0x', address: '0x' })).toThrow('Invalid address: 0x')
34 | })
35 | it('throws for invalid calldata', () => {
36 | expect(() =>
37 | toCallKey({
38 | address: '0x6b175474e89094c44da98b954eedeac495271d0f',
39 | callData: 'abc'
40 | })
41 | ).toThrow('Invalid hex: abc')
42 | })
43 | it('throws for uppercase hex', () => {
44 | expect(() =>
45 | toCallKey({
46 | address: '0x6b175474e89094c44da98b954eedeac495271d0f',
47 | callData: '0xabcD'
48 | })
49 | ).toThrow('Invalid hex: 0xabcD')
50 | })
51 | it('concatenates address to data', () => {
52 | expect(toCallKey({ address: '0x6b175474e89094c44da98b954eedeac495271d0f', callData: '0xabcd' })).toEqual(
53 | '0x6b175474e89094c44da98b954eedeac495271d0f-0xabcd'
54 | )
55 | })
56 | })
57 | })
58 |
--------------------------------------------------------------------------------
/src/state/multicall/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 |
3 | export interface Call {
4 | address: string
5 | callData: string
6 | }
7 |
8 | const ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/
9 | const LOWER_HEX_REGEX = /^0x[a-f0-9]*$/
10 | export function toCallKey(call: Call): string {
11 | if (!ADDRESS_REGEX.test(call.address)) {
12 | throw new Error(`Invalid address: ${call.address}`)
13 | }
14 | if (!LOWER_HEX_REGEX.test(call.callData)) {
15 | throw new Error(`Invalid hex: ${call.callData}`)
16 | }
17 | return `${call.address}-${call.callData}`
18 | }
19 |
20 | export function parseCallKey(callKey: string): Call {
21 | const pcs = callKey.split('-')
22 | if (pcs.length !== 2) {
23 | throw new Error(`Invalid call key: ${callKey}`)
24 | }
25 | return {
26 | address: pcs[0],
27 | callData: pcs[1]
28 | }
29 | }
30 |
31 | export interface ListenerOptions {
32 | // how often this data should be fetched, by default 1
33 | readonly blocksPerFetch?: number
34 | }
35 |
36 | export const addMulticallListeners = createAction<{ chainId: number; calls: Call[]; options?: ListenerOptions }>(
37 | 'multicall/addMulticallListeners'
38 | )
39 | export const removeMulticallListeners = createAction<{ chainId: number; calls: Call[]; options?: ListenerOptions }>(
40 | 'multicall/removeMulticallListeners'
41 | )
42 | export const fetchingMulticallResults = createAction<{ chainId: number; calls: Call[]; fetchingBlockNumber: number }>(
43 | 'multicall/fetchingMulticallResults'
44 | )
45 | export const errorFetchingMulticallResults = createAction<{
46 | chainId: number
47 | calls: Call[]
48 | fetchingBlockNumber: number
49 | }>('multicall/errorFetchingMulticallResults')
50 | export const updateMulticallResults = createAction<{
51 | chainId: number
52 | blockNumber: number
53 | results: {
54 | [callKey: string]: string | null
55 | }
56 | }>('multicall/updateMulticallResults')
57 |
--------------------------------------------------------------------------------
/src/state/pair/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 | import { Time } from 'lightweight-charts'
3 |
4 | export const updatePairChartData = createAction<{
5 | address: string
6 | chartData: Array>
7 | }>('pair/updatePairChartData')
8 |
9 | export const updatePairTokensChartData = createAction<{
10 | address: string
11 | chartData: Array>
12 | }>('pair/updatePairTokensChartData')
13 |
--------------------------------------------------------------------------------
/src/state/pair/reducer.ts:
--------------------------------------------------------------------------------
1 | import { createReducer } from '@reduxjs/toolkit'
2 | import { updatePairChartData, updatePairTokensChartData } from './actions'
3 | import { Time } from 'lightweight-charts'
4 |
5 | export interface ChartState {
6 | [address: string]: Array>
7 | }
8 |
9 | export interface TokenChartState {
10 | readonly pairData: ChartState
11 | readonly tokenPairData: ChartState
12 | }
13 |
14 | const initialState: TokenChartState = {
15 | pairData: {},
16 | tokenPairData: {}
17 | }
18 |
19 | export default createReducer(initialState, builder =>
20 | builder
21 | .addCase(updatePairChartData, (state, { payload: { address, chartData } }) => {
22 | const container = {} as ChartState
23 | container[address] = chartData
24 | const existingChartData = {
25 | ...(state.pairData || {}),
26 | ...container
27 | }
28 | state.pairData = existingChartData
29 | })
30 |
31 | .addCase(updatePairTokensChartData, (state, { payload: { address, chartData } }) => {
32 | const container = {} as ChartState
33 | container[address] = chartData
34 | const existingChartData = {
35 | ...(state.tokenPairData || {}),
36 | ...container
37 | }
38 | state.tokenPairData = existingChartData
39 | })
40 | )
41 |
--------------------------------------------------------------------------------
/src/state/stake/multiChainsHooks.ts:
--------------------------------------------------------------------------------
1 | import { ChainId } from '@pangolindex/sdk'
2 | import { useTotalPngEarned, useNearTotalPngEarned } from 'src/state/stake/hooks'
3 |
4 | export function useDummyHook() {
5 | return undefined
6 | }
7 |
8 | export type UseTotalPngEarnedHookType = {
9 | [chainId in ChainId]: typeof useTotalPngEarned | typeof useNearTotalPngEarned | typeof useDummyHook
10 | }
11 |
12 | export const useTotalPngEarnedHook: UseTotalPngEarnedHookType = {
13 | [ChainId.FUJI]: useTotalPngEarned,
14 | [ChainId.AVALANCHE]: useTotalPngEarned,
15 | [ChainId.WAGMI]: useTotalPngEarned,
16 | [ChainId.COSTON]: useTotalPngEarned,
17 | [ChainId.SONGBIRD]: useTotalPngEarned,
18 | [ChainId.FLARE_MAINNET]: useTotalPngEarned,
19 | [ChainId.HEDERA_TESTNET]: useTotalPngEarned,
20 | [ChainId.HEDERA_MAINNET]: useTotalPngEarned,
21 | [ChainId.NEAR_MAINNET]: useNearTotalPngEarned,
22 | [ChainId.NEAR_TESTNET]: useNearTotalPngEarned,
23 | [ChainId.COSTON2]: useTotalPngEarned,
24 | [ChainId.EVMOS_TESTNET]: useTotalPngEarned,
25 | [ChainId.EVMOS_MAINNET]: useDummyHook,
26 | [ChainId.ETHEREUM]: useDummyHook,
27 | [ChainId.POLYGON]: useDummyHook,
28 | [ChainId.FANTOM]: useDummyHook,
29 | [ChainId.XDAI]: useDummyHook,
30 | [ChainId.BSC]: useDummyHook,
31 | [ChainId.ARBITRUM]: useDummyHook,
32 | [ChainId.CELO]: useDummyHook,
33 | [ChainId.OKXCHAIN]: useDummyHook,
34 | [ChainId.VELAS]: useDummyHook,
35 | [ChainId.AURORA]: useDummyHook,
36 | [ChainId.CRONOS]: useDummyHook,
37 | [ChainId.FUSE]: useDummyHook,
38 | [ChainId.MOONRIVER]: useDummyHook,
39 | [ChainId.MOONBEAM]: useDummyHook,
40 | [ChainId.OP]: useDummyHook,
41 | [ChainId.SKALE_BELLATRIX_TESTNET]: useTotalPngEarned
42 | }
43 |
--------------------------------------------------------------------------------
/src/state/stake/singleSideConfig.ts:
--------------------------------------------------------------------------------
1 | import { ChainId, WAVAX, AVALANCHE_FUJI, Token } from '@pangolindex/sdk'
2 | import { Tokens } from '@honeycomb-finance/shared'
3 |
4 | const { PNG } = Tokens
5 |
6 | export interface SingleSideStaking {
7 | rewardToken: Token
8 | conversionRouteHops: Token[]
9 | stakingRewardAddress: string
10 | version: number
11 | }
12 |
13 | export const SINGLE_SIDE_STAKING: { [key: string]: SingleSideStaking } = {
14 | WAVAX_V0: {
15 | rewardToken: WAVAX[ChainId.AVALANCHE],
16 | conversionRouteHops: [],
17 | stakingRewardAddress: '0xD49B406A7A29D64e081164F6C3353C599A2EeAE9',
18 | version: 0
19 | },
20 | PNG_V0: {
21 | rewardToken: PNG[ChainId.AVALANCHE],
22 | conversionRouteHops: [WAVAX[ChainId.AVALANCHE]],
23 | stakingRewardAddress: '0x88afdaE1a9F58Da3E68584421937E5F564A0135b',
24 | version: 0
25 | }
26 | }
27 |
28 | export const SINGLE_SIDE_STAKING_V0: SingleSideStaking[] = Object.values(SINGLE_SIDE_STAKING).filter(
29 | staking => staking.version === 0
30 | )
31 |
32 | const FUJI_SINGLE_SIDE_STAKING: SingleSideStaking[] =
33 | AVALANCHE_FUJI.contracts?.staking
34 | ?.filter(contract => contract.active)
35 | .map(contract => ({
36 | rewardToken: PNG[ChainId.FUJI],
37 | conversionRouteHops: [WAVAX[ChainId.FUJI]],
38 | stakingRewardAddress: contract.address,
39 | version: 0
40 | })) ?? []
41 |
42 | export const SINGLE_SIDE_STAKING_REWARDS_INFO: {
43 | [chainId in ChainId]?: SingleSideStaking[][]
44 | } = {
45 | [ChainId.AVALANCHE]: [SINGLE_SIDE_STAKING_V0],
46 | [ChainId.FUJI]: [FUJI_SINGLE_SIDE_STAKING]
47 | }
48 |
--------------------------------------------------------------------------------
/src/state/token/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 |
3 | export const updateTokenWeeklyPriceChartData = createAction<{
4 | address: string
5 | chartData: Array<{ priceUSD: number; date: string }>
6 | }>('token/updateTokenWeeklyPriceChartData')
7 |
8 | export const updateTokenPriceChartData = createAction<{
9 | address: string
10 | chartData: Array<{ priceUSD: number; timestamp: string }>
11 | }>('token/updateTokenPriceChartData')
12 |
--------------------------------------------------------------------------------
/src/state/token/hooks.ts:
--------------------------------------------------------------------------------
1 | import { blockClients } from '../../apollo/client'
2 | import { GET_BLOCKS } from '../../apollo/block'
3 | import dayjs from 'dayjs'
4 | import utc from 'dayjs/plugin/utc'
5 | import { ChainId } from '@pangolindex/sdk'
6 | import { splitQuery } from '@honeycomb-finance/shared'
7 |
8 | dayjs.extend(utc)
9 |
10 | export async function getBlocksFromTimestamps(timestamps: Array, chainId: ChainId, skipCount = 500) {
11 | const blockClient = blockClients[chainId]
12 | if (timestamps?.length === 0 || !blockClient) {
13 | return []
14 | }
15 | const fetchedData: any = await splitQuery(GET_BLOCKS, blockClient, [], timestamps, skipCount)
16 | const blocks = []
17 | if (fetchedData) {
18 | for (const t in fetchedData) {
19 | if (fetchedData[t].length > 0) {
20 | blocks.push({
21 | timestamp: t.split('t')[1],
22 | number: fetchedData[t][0]['number']
23 | })
24 | }
25 | }
26 | }
27 |
28 | return blocks
29 | }
30 |
--------------------------------------------------------------------------------
/src/state/token/reducer.ts:
--------------------------------------------------------------------------------
1 | import { createReducer } from '@reduxjs/toolkit'
2 | import { updateTokenWeeklyPriceChartData, updateTokenPriceChartData } from './actions'
3 |
4 | export interface WeeklyState {
5 | [address: string]: Array<{ priceUSD: number; date: string }>
6 | }
7 |
8 | export interface ChartState {
9 | [address: string]: Array<{ priceUSD: number; timestamp: string }>
10 | }
11 |
12 | export interface TokenChartState {
13 | readonly weekly: WeeklyState
14 | readonly tokenPrices: ChartState
15 | }
16 |
17 | const initialState: TokenChartState = {
18 | weekly: {},
19 | tokenPrices: {}
20 | }
21 |
22 | export default createReducer(initialState, builder =>
23 | builder
24 |
25 | .addCase(updateTokenWeeklyPriceChartData, (state, { payload: { address, chartData } }) => {
26 | const container = {} as WeeklyState
27 | container[address] = chartData
28 | const existingChartData = {
29 | ...(state.weekly || {}),
30 | ...container
31 | }
32 | state.weekly = existingChartData
33 | })
34 |
35 | .addCase(updateTokenPriceChartData, (state, { payload: { address, chartData } }) => {
36 | const container = {} as ChartState
37 | container[address] = chartData
38 | const existingChartData = {
39 | ...(state.tokenPrices || {}),
40 | ...container
41 | }
42 | state.tokenPrices = existingChartData
43 | })
44 | )
45 |
--------------------------------------------------------------------------------
/src/state/transactions/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 | import { ChainId } from '@pangolindex/sdk'
3 |
4 | export interface SerializableTransactionReceipt {
5 | to: string
6 | from: string
7 | contractAddress: string
8 | transactionIndex: number
9 | blockHash: string
10 | transactionHash: string
11 | blockNumber: number
12 | status?: number
13 | }
14 |
15 | export const addTransaction = createAction<{
16 | chainId: ChainId
17 | hash: string
18 | from: string
19 | approval?: { tokenAddress: string; spender: string }
20 | claim?: { recipient: string }
21 | summary?: string
22 | }>('transactions/addTransaction')
23 | export const clearAllTransactions = createAction<{ chainId: ChainId }>('transactions/clearAllTransactions')
24 | export const finalizeTransaction = createAction<{
25 | chainId: ChainId
26 | hash: string
27 | receipt: SerializableTransactionReceipt
28 | }>('transactions/finalizeTransaction')
29 | export const checkedTransaction = createAction<{
30 | chainId: ChainId
31 | hash: string
32 | blockNumber: number
33 | }>('transactions/checkedTransaction')
34 |
--------------------------------------------------------------------------------
/src/state/transactions/reducer.ts:
--------------------------------------------------------------------------------
1 | import { createReducer } from '@reduxjs/toolkit'
2 | import {
3 | addTransaction,
4 | checkedTransaction,
5 | clearAllTransactions,
6 | finalizeTransaction,
7 | SerializableTransactionReceipt
8 | } from './actions'
9 |
10 | const now = () => new Date().getTime()
11 |
12 | export interface TransactionDetails {
13 | hash: string
14 | approval?: { tokenAddress: string; spender: string }
15 | summary?: string
16 | claim?: { recipient: string }
17 | receipt?: SerializableTransactionReceipt
18 | lastCheckedBlockNumber?: number
19 | addedTime: number
20 | confirmedTime?: number
21 | from: string
22 | }
23 |
24 | export interface TransactionState {
25 | [chainId: number]: {
26 | [txHash: string]: TransactionDetails
27 | }
28 | }
29 |
30 | export const initialState: TransactionState = {}
31 |
32 | export default createReducer(initialState, builder =>
33 | builder
34 | .addCase(addTransaction, (transactions, { payload: { chainId, from, hash, approval, summary, claim } }) => {
35 | if (transactions[chainId]?.[hash]) {
36 | throw Error('Attempted to add existing transaction.')
37 | }
38 | const txs = transactions[chainId] ?? {}
39 | txs[hash] = { hash, approval, summary, claim, from, addedTime: now() }
40 | transactions[chainId] = txs
41 | })
42 | .addCase(clearAllTransactions, (transactions, { payload: { chainId } }) => {
43 | if (!transactions[chainId]) return
44 | transactions[chainId] = {}
45 | })
46 | .addCase(checkedTransaction, (transactions, { payload: { chainId, hash, blockNumber } }) => {
47 | const tx = transactions[chainId]?.[hash]
48 | if (!tx) {
49 | return
50 | }
51 | if (!tx.lastCheckedBlockNumber) {
52 | tx.lastCheckedBlockNumber = blockNumber
53 | } else {
54 | tx.lastCheckedBlockNumber = Math.max(blockNumber, tx.lastCheckedBlockNumber)
55 | }
56 | })
57 | .addCase(finalizeTransaction, (transactions, { payload: { hash, chainId, receipt } }) => {
58 | const tx = transactions[chainId]?.[hash]
59 | if (!tx) {
60 | return
61 | }
62 | tx.receipt = receipt
63 | tx.confirmedTime = now()
64 | })
65 | )
66 |
--------------------------------------------------------------------------------
/src/state/transactions/updater.test.ts:
--------------------------------------------------------------------------------
1 | // import { shouldCheck } from './updater'
2 |
3 | describe('transactions updater', () => {
4 | describe('shouldCheck', () => {
5 | it('returns true if no receipt and never checked', () => {
6 | // expect(shouldCheck(10, { addedTime: 100 })).toEqual(true)
7 | })
8 | // it('returns false if has receipt and never checked', () => {
9 | // expect(shouldCheck(10, { addedTime: 100, receipt: {} })).toEqual(false)
10 | // })
11 | // it('returns true if has not been checked in 1 blocks', () => {
12 | // expect(shouldCheck(10, { addedTime: new Date().getTime(), lastCheckedBlockNumber: 9 })).toEqual(true)
13 | // })
14 | // it('returns false if checked in last 3 blocks and greater than 20 minutes old', () => {
15 | // expect(shouldCheck(10, { addedTime: new Date().getTime() - 21 * 60 * 1000, lastCheckedBlockNumber: 8 })).toEqual(
16 | // false
17 | // )
18 | // })
19 | // it('returns true if not checked in last 5 blocks and greater than 20 minutes old', () => {
20 | // expect(shouldCheck(10, { addedTime: new Date().getTime() - 21 * 60 * 1000, lastCheckedBlockNumber: 5 })).toEqual(
21 | // true
22 | // )
23 | // })
24 | // it('returns false if checked in last 10 blocks and greater than 60 minutes old', () => {
25 | // expect(shouldCheck(20, { addedTime: new Date().getTime() - 61 * 60 * 1000, lastCheckedBlockNumber: 11 })).toEqual(
26 | // false
27 | // )
28 | // })
29 | // it('returns true if checked in last 3 blocks and greater than 20 minutes old', () => {
30 | // expect(shouldCheck(20, { addedTime: new Date().getTime() - 61 * 60 * 1000, lastCheckedBlockNumber: 10 })).toEqual(
31 | // true
32 | // )
33 | // })
34 | })
35 | })
36 |
--------------------------------------------------------------------------------
/src/state/user/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 |
3 | export const updateMatchesDarkMode = createAction<{ matchesDarkMode: boolean }>('user/updateMatchesDarkMode')
4 | export const updateUserDarkMode = createAction<{ userDarkMode: boolean }>('user/updateUserDarkMode')
5 | export const toggleURLWarning = createAction('app/toggleURLWarning')
6 | export const updateWallet = createAction<{ wallet: string | null }>('user/updateWallet')
7 |
--------------------------------------------------------------------------------
/src/state/user/hooks.tsx:
--------------------------------------------------------------------------------
1 | import { useCallback } from 'react'
2 | import { shallowEqual } from 'react-redux'
3 | import { AppState, useDispatch, useSelector } from '../index'
4 | import { updateUserDarkMode, toggleURLWarning, updateWallet } from './actions'
5 |
6 | export function useIsDarkMode(): boolean {
7 | const { userDarkMode, matchesDarkMode } = useSelector<{ userDarkMode: boolean | null; matchesDarkMode: boolean }>(
8 | ({ user: { matchesDarkMode, userDarkMode } }) => ({
9 | userDarkMode,
10 | matchesDarkMode
11 | }),
12 | shallowEqual
13 | )
14 |
15 | if (userDarkMode === null && !matchesDarkMode) {
16 | // by default we want to show dark mode
17 | return true
18 | }
19 |
20 | return userDarkMode === null ? matchesDarkMode : userDarkMode
21 | }
22 |
23 | export function useDarkModeManager(): [boolean, () => void] {
24 | const dispatch = useDispatch()
25 | const darkMode = useIsDarkMode()
26 |
27 | const toggleSetDarkMode = useCallback(() => {
28 | dispatch(updateUserDarkMode({ userDarkMode: !darkMode }))
29 | }, [darkMode, dispatch])
30 |
31 | return [darkMode, toggleSetDarkMode]
32 | }
33 |
34 | export function useURLWarningVisible(): boolean {
35 | return useSelector((state: AppState) => state.user.URLWarningVisible)
36 | }
37 |
38 | export function useURLWarningToggle(): () => void {
39 | const dispatch = useDispatch()
40 | return useCallback(() => dispatch(toggleURLWarning()), [dispatch])
41 | }
42 |
43 | export function useWallet(): [string | null, (wallet: string | null) => void] {
44 | const dispatch = useDispatch()
45 | const wallet = useSelector(state => state.user.wallet)
46 |
47 | const setWallet = useCallback(
48 | (walletKey: string | null) => {
49 | dispatch(updateWallet({ wallet: walletKey }))
50 | },
51 | [dispatch]
52 | )
53 |
54 | return [wallet, setWallet]
55 | }
56 |
--------------------------------------------------------------------------------
/src/state/user/reducer.test.ts:
--------------------------------------------------------------------------------
1 | import { createStore, Store } from 'redux'
2 | import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE } from '../../constants'
3 | import { updateVersion } from '../global/actions'
4 | import reducer, { initialState, UserState } from './reducer'
5 |
6 | describe('swap reducer', () => {
7 | let store: Store
8 |
9 | beforeEach(() => {
10 | store = createStore(reducer, initialState)
11 | })
12 |
13 | describe('updateVersion', () => {
14 | it('has no timestamp originally', () => {
15 | expect(store.getState().lastUpdateVersionTimestamp).toBeUndefined()
16 | })
17 | it('sets the lastUpdateVersionTimestamp', () => {
18 | const time = new Date().getTime()
19 | store.dispatch(updateVersion())
20 | expect(store.getState().lastUpdateVersionTimestamp).toBeGreaterThanOrEqual(time)
21 | })
22 | it('sets allowed slippage and deadline', () => {
23 | store = createStore(reducer, {
24 | ...initialState,
25 | userDeadline: undefined,
26 | userSlippageTolerance: undefined
27 | } as any)
28 | store.dispatch(updateVersion())
29 | expect(store.getState().userDeadline).toEqual(DEFAULT_DEADLINE_FROM_NOW)
30 | expect(store.getState().userSlippageTolerance).toEqual(INITIAL_ALLOWED_SLIPPAGE)
31 | })
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/src/state/user/updater.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useDispatch } from 'src/state'
3 | import { updateMatchesDarkMode } from './actions'
4 |
5 | export default function Updater(): null {
6 | const dispatch = useDispatch()
7 |
8 | // keep dark mode in sync with the system
9 | useEffect(() => {
10 | const darkHandler = (_match: MediaQueryListEvent) => {
11 | dispatch(updateMatchesDarkMode({ matchesDarkMode: _match.matches }))
12 | }
13 |
14 | const match = window?.matchMedia('(prefers-color-scheme: dark)')
15 | dispatch(updateMatchesDarkMode({ matchesDarkMode: match.matches }))
16 |
17 | if (match?.addEventListener) {
18 | match?.addEventListener('change', darkHandler)
19 | }
20 |
21 | return () => {
22 | if (match?.removeEventListener) {
23 | match?.removeEventListener('change', darkHandler)
24 | }
25 | }
26 | }, [dispatch])
27 |
28 | return null
29 | }
30 |
--------------------------------------------------------------------------------
/src/state/watchlists/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 |
3 | export const addCurrency = createAction('watchlists/addCurrency')
4 | export const removeCurrency = createAction('watchlists/removeCurrency')
5 |
--------------------------------------------------------------------------------
/src/state/watchlists/reducer.ts:
--------------------------------------------------------------------------------
1 | import { createReducer } from '@reduxjs/toolkit'
2 | import { addCurrency, removeCurrency } from './actions'
3 |
4 | export interface WatchlistState {
5 | readonly currencies: string[]
6 | }
7 |
8 | const initialState: WatchlistState = {
9 | currencies: []
10 | }
11 |
12 | export default createReducer(initialState, builder =>
13 | builder
14 | .addCase(addCurrency, (state, { payload: address }) => {
15 | const existingSelectedListUrl = ([] as string[]).concat(state.currencies || [])
16 |
17 | existingSelectedListUrl.push(address)
18 | state.currencies = existingSelectedListUrl
19 | })
20 | .addCase(removeCurrency, (state, { payload: address }) => {
21 | const existingList = ([] as string[]).concat(state.currencies || [])
22 | const index = existingList.indexOf(address)
23 |
24 | if (index !== -1) {
25 | if (existingList?.length === 1) {
26 | // if user want to remove the list and if there is only one item in the selected list
27 | state.currencies = [] as string[]
28 | } else {
29 | existingList.splice(index, 1)
30 | state.currencies = existingList
31 | }
32 | }
33 | })
34 | )
35 |
--------------------------------------------------------------------------------
/src/utils/getLibrary.ts:
--------------------------------------------------------------------------------
1 | import { Web3Provider } from '@ethersproject/providers'
2 |
3 | export default function getLibrary(provider: any): Web3Provider {
4 | try {
5 | const library = new Web3Provider(provider, 'any')
6 | library.pollingInterval = 15000
7 | return library
8 | } catch (error) {
9 | return provider
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/utils/retry.test.ts:
--------------------------------------------------------------------------------
1 | import { retry, RetryableError } from './retry'
2 |
3 | describe('retry', () => {
4 | function makeFn(fails: number, result: T, retryable = true): () => Promise {
5 | return async () => {
6 | if (fails > 0) {
7 | fails--
8 | throw retryable ? new RetryableError('failure') : new Error('bad failure')
9 | }
10 | return result
11 | }
12 | }
13 |
14 | it('fails for non-retryable error', async () => {
15 | await expect(retry(makeFn(1, 'abc', false), { n: 3, maxWait: 0, minWait: 0 }).promise).rejects.toThrow(
16 | 'bad failure'
17 | )
18 | })
19 |
20 | it('works after one fail', async () => {
21 | await expect(retry(makeFn(1, 'abc'), { n: 3, maxWait: 0, minWait: 0 }).promise).resolves.toEqual('abc')
22 | })
23 |
24 | it('works after two fails', async () => {
25 | await expect(retry(makeFn(2, 'abc'), { n: 3, maxWait: 0, minWait: 0 }).promise).resolves.toEqual('abc')
26 | })
27 |
28 | it('throws if too many fails', async () => {
29 | await expect(retry(makeFn(4, 'abc'), { n: 3, maxWait: 0, minWait: 0 }).promise).rejects.toThrow('failure')
30 | })
31 |
32 | it('cancel causes promise to reject', async () => {
33 | const { promise, cancel } = retry(makeFn(2, 'abc'), { n: 3, minWait: 100, maxWait: 100 })
34 | cancel()
35 | await expect(promise).rejects.toThrow('Cancelled')
36 | })
37 |
38 | it('cancel no-op after complete', async () => {
39 | const { promise, cancel } = retry(makeFn(0, 'abc'), { n: 3, minWait: 100, maxWait: 100 })
40 | // defer
41 | setTimeout(cancel, 0)
42 | await expect(promise).resolves.toEqual('abc')
43 | })
44 |
45 | async function checkTime(fn: () => Promise, min: number, max: number) {
46 | const time = new Date().getTime()
47 | await fn()
48 | const diff = new Date().getTime() - time
49 | expect(diff).toBeGreaterThanOrEqual(min)
50 | expect(diff).toBeLessThanOrEqual(max)
51 | }
52 |
53 | it('waits random amount of time between min and max', async () => {
54 | const promises = []
55 | for (let i = 0; i < 10; i++) {
56 | promises.push(
57 | checkTime(
58 | () => expect(retry(makeFn(4, 'abc'), { n: 3, maxWait: 100, minWait: 50 }).promise).rejects.toThrow('failure'),
59 | 150,
60 | 400
61 | )
62 | )
63 | }
64 | await Promise.all(promises)
65 | })
66 | })
67 |
--------------------------------------------------------------------------------
/src/utils/retry.ts:
--------------------------------------------------------------------------------
1 | export function wait(ms: number): Promise {
2 | return new Promise(resolve => setTimeout(resolve, ms))
3 | }
4 |
5 | function waitRandom(min: number, max: number): Promise {
6 | return wait(min + Math.round(Math.random() * Math.max(0, max - min)))
7 | }
8 |
9 | /**
10 | * This error is thrown if the function is cancelled before completing
11 | */
12 | export class CancelledError extends Error {
13 | constructor() {
14 | super('Cancelled')
15 | }
16 | }
17 |
18 | /**
19 | * Throw this error if the function should retry
20 | */
21 | export class RetryableError extends Error {}
22 |
23 | /**
24 | * Retries the function that returns the promise until the promise successfully resolves up to n retries
25 | * @param fn function to retry
26 | * @param n how many times to retry
27 | * @param minWait min wait between retries in ms
28 | * @param maxWait max wait between retries in ms
29 | */
30 | export function retry(
31 | fn: () => Promise,
32 | { n, minWait, maxWait }: { n: number; minWait: number; maxWait: number }
33 | ): { promise: Promise; cancel: () => void } {
34 | let completed = false
35 | let rejectCancelled: (error: Error) => void
36 | const promise = new Promise(async (resolve, reject) => {
37 | rejectCancelled = reject
38 | while (true) {
39 | let result: T
40 | try {
41 | result = await fn()
42 | if (!completed) {
43 | resolve(result)
44 | completed = true
45 | }
46 | break
47 | } catch (error) {
48 | if (completed) {
49 | break
50 | }
51 | if (n <= 0 || !(error instanceof RetryableError)) {
52 | reject(error)
53 | completed = true
54 | break
55 | }
56 | n--
57 | }
58 | await waitRandom(minWait, maxWait)
59 | }
60 | })
61 | return {
62 | promise,
63 | cancel: () => {
64 | if (completed) return
65 | completed = true
66 | rejectCancelled(new CancelledError())
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "noEmit": true,
13 | "esModuleInterop": true,
14 | "module": "esnext",
15 | "strict": true,
16 | "alwaysStrict": true,
17 | "strictNullChecks": true,
18 | "noUnusedLocals": true,
19 | "noFallthroughCasesInSwitch": true,
20 | "noImplicitAny": true,
21 | "noImplicitThis": true,
22 | "noImplicitReturns": true,
23 | "moduleResolution": "node",
24 | "resolveJsonModule": true,
25 | "isolatedModules": true,
26 | "jsx": "react",
27 | "downlevelIteration": true,
28 | "allowSyntheticDefaultImports": true,
29 | "types": [
30 | "react-spring",
31 | "jest"
32 | ],
33 | "baseUrl": "."
34 | },
35 | "exclude": [
36 | "node_modules",
37 | "cypress",
38 | "./src/**/*.test.ts"
39 | ],
40 | "include": [
41 | "./src/**/*.ts",
42 | "./src/**/*.tsx",
43 | "src/components/Confetti/index.js"
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/wrangler.toml:
--------------------------------------------------------------------------------
1 | name = "pangolin-app-temp"
2 | type = "javascript"
3 | route = "app.pangolin.exchange/*"
4 |
5 | [site]
6 | bucket = "./build"
--------------------------------------------------------------------------------