├── .eslintrc.json
├── .firebaserc
├── .github
└── workflows
│ ├── gcp-deploy.yaml
│ ├── gcp-undeploy.yaml
│ ├── production.yml
│ └── sokol.yml
├── .gitignore
├── .husky
├── .gitignore
└── pre-commit
├── .prettierrc.json
├── Dockerfile
├── README.md
├── docker-compose.yml
├── firebase.json
├── nginx.conf
├── package.json
├── packages
├── dapp
│ ├── .eslintrc.json
│ ├── .sample-env
│ ├── .unimportedrc.json
│ ├── jsconfig.json
│ ├── package.json
│ ├── public
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon.png
│ │ ├── browserconfig.xml
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── mstile-150x150.png
│ │ ├── robots.txt
│ │ └── safari-pinned-tab.svg
│ └── src
│ │ ├── App.jsx
│ │ ├── assets
│ │ ├── alert.svg
│ │ ├── blue-tick.svg
│ │ ├── bsc-logo.png
│ │ ├── change-network.png
│ │ ├── claim.svg
│ │ ├── close.svg
│ │ ├── coinbase.svg
│ │ ├── confirm-transfer.svg
│ │ ├── custom-token.svg
│ │ ├── down-triangle.svg
│ │ ├── drop-down.svg
│ │ ├── error.svg
│ │ ├── eth-logo.png
│ │ ├── eth.png
│ │ ├── imtoken.svg
│ │ ├── info.svg
│ │ ├── loading.svg
│ │ ├── logo.svg
│ │ ├── metamask-fox.svg
│ │ ├── multiple-claim.svg
│ │ ├── no-history.svg
│ │ ├── right-arrow.svg
│ │ ├── search.svg
│ │ ├── settings.svg
│ │ ├── transfer.svg
│ │ ├── unlock.svg
│ │ ├── up-triangle.svg
│ │ └── xdai-logo.png
│ │ ├── components
│ │ ├── bridge
│ │ │ ├── ActionButtons.jsx
│ │ │ ├── AdvancedMenu.jsx
│ │ │ ├── BridgeTokens.jsx
│ │ │ ├── FromToken.jsx
│ │ │ ├── SwitchButton.jsx
│ │ │ ├── SystemFeedback.jsx
│ │ │ ├── ToToken.jsx
│ │ │ ├── TransferButton.jsx
│ │ │ └── UnlockButton.jsx
│ │ ├── common
│ │ │ ├── AddToMetamask.jsx
│ │ │ ├── BridgeDropdown.jsx
│ │ │ ├── CoinzillaBannerAd.jsx
│ │ │ ├── CoinzillaTextAd.jsx
│ │ │ ├── ErrorFallback.jsx
│ │ │ ├── Footer.jsx
│ │ │ ├── Header.jsx
│ │ │ ├── Layout.jsx
│ │ │ ├── Logo.jsx
│ │ │ ├── ProgressRing.jsx
│ │ │ ├── Routes.jsx
│ │ │ ├── TxLink.jsx
│ │ │ ├── UpdateSettings.jsx
│ │ │ ├── WalletInfo.jsx
│ │ │ └── WalletSelector.jsx
│ │ ├── history
│ │ │ ├── BridgeHistory.jsx
│ │ │ ├── HistoryItem.jsx
│ │ │ ├── HistoryPagination.jsx
│ │ │ ├── ManualClaim.jsx
│ │ │ └── NoHistory.jsx
│ │ ├── modals
│ │ │ ├── AddToMetamaskModal.jsx
│ │ │ ├── BridgeLoadingModal.jsx
│ │ │ ├── ClaimErrorModal.jsx
│ │ │ ├── ClaimTokensModal.jsx
│ │ │ ├── ClaimTransferModal.jsx
│ │ │ ├── ConfirmBSCTokenModal.jsx
│ │ │ ├── ConfirmTransferModal.jsx
│ │ │ ├── CustomTokenModal.jsx
│ │ │ ├── LoadingModal.jsx
│ │ │ ├── NeedsConfirmationModal.jsx
│ │ │ ├── SelectTokenModal.jsx
│ │ │ ├── TermsOfServiceModal.jsx
│ │ │ └── TokenListModal.jsx
│ │ └── warnings
│ │ │ ├── AuspiciousGasWarning.jsx
│ │ │ ├── BSCETHTokenWarnings.jsx
│ │ │ ├── BSCGCTokenWarnings.jsx
│ │ │ ├── DaiWarning.jsx
│ │ │ ├── GCOriginOnBSCTokenWarning.jsx
│ │ │ ├── GnosisSafeWarning.jsx
│ │ │ ├── GraphHealthWarning.jsx
│ │ │ ├── InflationaryTokenWarning.jsx
│ │ │ ├── MedianGasWarning.jsx
│ │ │ ├── NeedsTransactionsWarning.jsx
│ │ │ ├── RPCHealthWarning.jsx
│ │ │ ├── RebasingTokenWarning.jsx
│ │ │ ├── SafeMoonTokenWarning.jsx
│ │ │ ├── StakeTokenWarning.jsx
│ │ │ └── TokenWarnings.jsx
│ │ ├── contexts
│ │ ├── BridgeContext.jsx
│ │ ├── SettingsContext.jsx
│ │ └── Web3Context.jsx
│ │ ├── hooks
│ │ ├── useAmbVersion.js
│ │ ├── useApproval.js
│ │ ├── useBridgeDirection.js
│ │ ├── useClaim.js
│ │ ├── useClaimableTransfers.js
│ │ ├── useCoinzillaText.js
│ │ ├── useCopyToClipboard.js
│ │ ├── useENS.js
│ │ ├── useETHPrice.js
│ │ ├── useGasPrice.js
│ │ ├── useGraphHealth.js
│ │ ├── useLocalState.js
│ │ ├── useMediatorInfo.js
│ │ ├── useNeedsClaiming.js
│ │ ├── useRPCHealth.js
│ │ ├── useRefresh.js
│ │ ├── useRenderChain.jsx
│ │ ├── useSwitchChain.js
│ │ ├── useTokenDisabled.jsx
│ │ ├── useTokenLimits.js
│ │ ├── useTotalConfirms.js
│ │ ├── useTransactionStatus.js
│ │ ├── useUpdateInterval.js
│ │ ├── useUserHistory.js
│ │ └── useValidatorsContract.js
│ │ ├── icons
│ │ ├── DownArrowIcon.jsx
│ │ ├── GithubIcon.jsx
│ │ ├── GnosisChainIcon.jsx
│ │ ├── HistoryIcon.jsx
│ │ ├── LeftIcon.jsx
│ │ ├── LimitsIcon.jsx
│ │ ├── NetworkIcon.jsx
│ │ ├── OmniBridgeIcon.jsx
│ │ ├── PlusIcon.jsx
│ │ ├── RaidGuildIcon.jsx
│ │ ├── RightIcon.jsx
│ │ ├── SettingsIcon.jsx
│ │ ├── SwitchIcon.jsx
│ │ ├── TelegramIcon.jsx
│ │ ├── TwitterIcon.jsx
│ │ ├── WalletFilledIcon.jsx
│ │ └── WalletIcon.jsx
│ │ ├── index.jsx
│ │ ├── lib
│ │ ├── amb.js
│ │ ├── bridge.js
│ │ ├── chainalysis.js
│ │ ├── constants.js
│ │ ├── graphHealth.js
│ │ ├── helpers.js
│ │ ├── history.js
│ │ ├── message.js
│ │ ├── metamask.js
│ │ ├── networks.js
│ │ ├── overrides.js
│ │ ├── providerHelpers.js
│ │ ├── providers.js
│ │ ├── token.js
│ │ └── tokenList.js
│ │ ├── pages
│ │ ├── History.jsx
│ │ └── Home.jsx
│ │ ├── snapshots
│ │ ├── signatures_0x258667E543C913264388B33328337257aF208a8f.json
│ │ ├── signatures_0x459A3bd49F1ff109bc90b76125533699AaAAf9A6.json
│ │ ├── signatures_0x674c97db4cE6caC04A124d745979f3E4cBa0E9f0.json
│ │ └── signatures_0xbDc141c8D2343f33F40Cb9edD601CcF460CD0dDe.json
│ │ ├── stores
│ │ ├── ethPrice.js
│ │ ├── gasPrice.js
│ │ ├── graphHealth.js
│ │ └── rpcHealth.js
│ │ └── theme.js
└── subgraph
│ ├── config
│ ├── bsc-mainnet.json
│ ├── bsc-xdai.json
│ ├── kovan.json
│ ├── mainnet-bsc.json
│ ├── mainnet.json
│ ├── poa-xdai.json
│ ├── sokol.json
│ ├── xdai-bsc.json
│ ├── xdai-poa.json
│ └── xdai.json
│ ├── package.json
│ ├── schema.graphql
│ ├── src
│ ├── abis
│ │ ├── amb.json
│ │ ├── bridge.json
│ │ ├── omnibridge.json
│ │ └── token.json
│ └── mappings
│ │ ├── amb.ts
│ │ ├── bridge.ts
│ │ ├── helpers.ts
│ │ ├── omnibridge.ts
│ │ └── overrides.ts
│ └── subgraph.template.yaml
└── yarn.lock
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["airbnb", "prettier"],
4 | "parserOptions": {
5 | "ecmaVersion": 6,
6 | "sourceType": "module"
7 | },
8 | "plugins": ["simple-import-sort"],
9 | "settings": {},
10 | "rules": {
11 | "import/no-default-export": "error",
12 | "import/prefer-default-export": "off",
13 | "sort-imports": "off",
14 | "import/order": "off",
15 | "simple-import-sort/imports": "warn",
16 | "simple-import-sort/exports": "warn"
17 | },
18 | "ignorePatterns": ["packages/subgraph/**"]
19 | }
20 |
--------------------------------------------------------------------------------
/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "xdai": "xdai-omnibridge",
4 | "sokol": "sokol-omnibridge"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.github/workflows/gcp-undeploy.yaml:
--------------------------------------------------------------------------------
1 | name: Undeploy Preview from Cloud Run
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - develop
7 | types:
8 | - closed
9 |
10 | jobs:
11 | undeploy:
12 | name: Undeploy from Cloud Run
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - id: auth
17 | uses: google-github-actions/auth@v0
18 | with:
19 | credentials_json: ${{ secrets.GCP_CREDENTIALS }}
20 |
21 | - name: Set up gcloud
22 | uses: google-github-actions/setup-gcloud@v0
23 |
24 | - name: Undeploy
25 | run: gcloud -q run services delete omnibridge-ui-preview-${{github.event.number}} --region asia-east1
26 |
27 | - name: Delete image
28 | run: gcloud -q artifacts docker images delete asia-east1-docker.pkg.dev/omnibridge-ui/frontend/dev-image-${{github.event.number}}
29 |
30 | - name: Comment on pull request
31 | uses: thollander/actions-comment-pull-request@v1
32 | with:
33 | message: Successfully undeployed the Preview of this Pull Request
34 | GITHUB_TOKEN: ${{github.token}}
35 |
--------------------------------------------------------------------------------
/.github/workflows/production.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy Production
2 | on:
3 | push:
4 | branches:
5 | - master
6 |
7 | jobs:
8 | deploy:
9 | name: Build & Deploy Production
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Cancel Previous Runs
13 | uses: styfle/cancel-workflow-action@0.4.0
14 | with:
15 | access_token: ${{ github.token }}
16 | - uses: actions/checkout@v2
17 | - uses: actions/setup-node@v1
18 | with:
19 | node-version: 12.x
20 |
21 | - uses: actions/cache@v2
22 | with:
23 | path: '**/node_modules'
24 | key: nodeModules-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
25 |
26 | - name: Install Dependencies
27 | run: yarn --frozen-lockfile
28 | env:
29 | CI: true
30 |
31 | - name: Build
32 | run: yarn dapp:build
33 | env:
34 | CI: true
35 | REACT_APP_DEFAULT_BRIDGE_DIRECTION: eth-xdai
36 | REACT_APP_ENABLED_BRIDGES: 'eth-xdai bsc-xdai poa-xdai eth-bsc'
37 | REACT_APP_INFURA_ID: ${{ secrets.INFURA_ID }}
38 | REACT_APP_GAS_PRICE_FALLBACK_GWEI: 50
39 | REACT_APP_GAS_PRICE_SUPPLIER_URL: https://gasprice.poa.network/
40 | REACT_APP_GAS_PRICE_SPEED_TYPE: fast
41 | REACT_APP_GAS_PRICE_UPDATE_INTERVAL: 60000
42 | REACT_APP_ETH_PRICE_API_URL: 'https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=USD'
43 | REACT_APP_ETH_PRICE_UPDATE_INTERVAL: 60000
44 | REACT_APP_TITLE: OmniBridge - %c
45 | REACT_APP_DESCRIPTION: 'The OmniBridge multi-token extension is the simplest way to transfer ANY ERC20/ERC677/ERC827 token to and from the xDai chain.'
46 | REACT_APP_UI_STATUS_UPDATE_INTERVAL: 5000
47 | REACT_APP_DEBUG_LOGS: false
48 | REACT_APP_GRAPH_HEALTH_UPDATE_INTERVAL: 60000
49 | REACT_APP_GRAPH_HEALTH_THRESHOLD_BLOCKS: 10
50 | REACT_APP_XDAI_RPC_URL: https://rpc.ankr.com/gnosis
51 | REACT_APP_POA_RPC_URL: https://core.poa.network
52 | REACT_APP_SOKOL_RPC_URL: https://sokol.poa.network
53 | REACT_APP_MAINNET_RPC_URL: https://mainnet.infura.io/v3/${{ secrets.INFURA_ID }} https://mainnet-nethermind.blockscout.com/
54 | REACT_APP_KOVAN_RPC_URL: https://kovan.infura.io/v3/${{ secrets.INFURA_ID }} https://kovan.poa.network/
55 | REACT_APP_BSC_RPC_URL: https://bsc-dataseed.binance.org https://bsc-dataseed1.defibit.io/
56 | REACT_APP_COINZILLA_API_KEY: ${{ secrets.COINZILLA_API_KEY }}
57 | REACT_APP_OWLRACLE_API_KEY: ${{ secrets.OWLRACLE_API_KEY }}
58 | REACT_APP_SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
59 |
60 | - name: Deploy to Firebase
61 | uses: w9jds/firebase-action@master
62 | with:
63 | args: deploy --only hosting --project xdai
64 | env:
65 | CI: true
66 | FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
67 |
68 | - name: Create Sentry release
69 | uses: getsentry/action-release@v1
70 | env:
71 | SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
72 | SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
73 | SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
74 | with:
75 | environment: production
76 |
--------------------------------------------------------------------------------
/.github/workflows/sokol.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy Sokol
2 | on:
3 | push:
4 | branches:
5 | - develop
6 |
7 | jobs:
8 | deploy:
9 | name: Build & Deploy Sokol
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Cancel Previous Runs
13 | uses: styfle/cancel-workflow-action@0.4.0
14 | with:
15 | access_token: ${{ github.token }}
16 | - uses: actions/checkout@v2
17 | - uses: actions/setup-node@v1
18 | with:
19 | node-version: 12.x
20 |
21 | - uses: actions/cache@v2
22 | with:
23 | path: '**/node_modules'
24 | key: nodeModules-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
25 |
26 | - name: Install Dependencies
27 | run: yarn --frozen-lockfile
28 | env:
29 | CI: true
30 |
31 | - name: Build
32 | run: yarn dapp:build
33 | env:
34 | CI: true
35 | REACT_APP_DEFAULT_BRIDGE_DIRECTION: kovan-sokol
36 | REACT_APP_ENABLED_BRIDGES: 'eth-xdai bsc-xdai poa-xdai eth-bsc kovan-sokol'
37 | REACT_APP_INFURA_ID: ${{ secrets.PERSONAL_INFURA_ID }}
38 | REACT_APP_GAS_PRICE_FALLBACK_GWEI: 50
39 | REACT_APP_GAS_PRICE_SUPPLIER_URL: https://gasprice.poa.network/
40 | REACT_APP_GAS_PRICE_SPEED_TYPE: fast
41 | REACT_APP_GAS_PRICE_UPDATE_INTERVAL: 60000
42 | REACT_APP_ETH_PRICE_API_URL: 'https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=USD'
43 | REACT_APP_ETH_PRICE_UPDATE_INTERVAL: 60000
44 | REACT_APP_TITLE: OmniBridge - %c
45 | REACT_APP_DESCRIPTION: 'The OmniBridge multi-token extension is the simplest way to transfer ANY ERC20/ERC677/ERC827 token to and from the xDai chain.'
46 | REACT_APP_UI_STATUS_UPDATE_INTERVAL: 5000
47 | REACT_APP_DEBUG_LOGS: true
48 | REACT_APP_GRAPH_HEALTH_UPDATE_INTERVAL: 60000
49 | REACT_APP_GRAPH_HEALTH_THRESHOLD_BLOCKS: 10
50 | REACT_APP_XDAI_RPC_URL: https://rpc.ankr.com/gnosis
51 | REACT_APP_POA_RPC_URL: https://core.poa.network
52 | REACT_APP_SOKOL_RPC_URL: https://sokol.poa.network
53 | REACT_APP_MAINNET_RPC_URL: https://mainnet.infura.io/v3/${{ secrets.PERSONAL_INFURA_ID }} https://mainnet-nethermind.blockscout.com/
54 | REACT_APP_KOVAN_RPC_URL: https://kovan.infura.io/v3/${{ secrets.PERSONAL_INFURA_ID }} https://kovan.poa.network/
55 | REACT_APP_BSC_RPC_URL: https://bsc-dataseed.binance.org https://bsc-dataseed1.defibit.io/
56 | REACT_APP_COINZILLA_API_KEY: ${{ secrets.COINZILLA_API_KEY }}
57 | REACT_APP_OWLRACLE_API_KEY: ${{ secrets.OWLRACLE_API_KEY }}
58 | REACT_APP_SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
59 |
60 | - name: Deploy to Firebase
61 | uses: w9jds/firebase-action@master
62 | with:
63 | args: deploy --only hosting --project sokol
64 | env:
65 | CI: true
66 | FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
67 |
68 | - name: Create Sentry release
69 | uses: getsentry/action-release@v1
70 | env:
71 | SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
72 | SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
73 | SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
74 | with:
75 | environment: sokol
76 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # testing
9 | coverage
10 |
11 | # production
12 | build
13 |
14 | # misc
15 | .DS_Store
16 | .env*
17 |
18 | # debug
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | # subgraph
24 | packages/subgraph/build/
25 | packages/subgraph/src/types/
26 |
27 | # vim
28 | *.swp
29 |
30 | # firebase
31 | .firebase/*
32 | firebase-debug.log*
33 |
34 | # eslint
35 | .eslintcache
36 |
37 | # subgraph
38 | subgraph.yaml
39 |
40 | # package-lock
41 | package-lock.json
42 |
43 | # yarn
44 | .yarn*
45 | .yarnrc
46 |
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | yarn lint-staged
5 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "trailingComma": "all",
5 | "tabWidth": 2,
6 | "arrowParens": "avoid",
7 | "overrides": [
8 | {
9 | "files": "overrides.js",
10 | "options": {
11 | "printWidth": 90
12 | }
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Stage 1 - the build process
2 |
3 | FROM node:16 AS build
4 |
5 | WORKDIR /usr/src/app
6 |
7 | COPY yarn.lock .
8 | COPY package.json .
9 | COPY .eslintrc.json .
10 | COPY packages/dapp/src ./packages/dapp/src
11 | COPY packages/dapp/public ./packages/dapp/public
12 | COPY packages/dapp/package.json ./packages/dapp/package.json
13 | COPY packages/dapp/jsconfig.json ./packages/dapp/jsconfig.json
14 | COPY packages/dapp/.eslintrc.json ./packages/dapp/.eslintrc.json
15 |
16 | ARG REACT_APP_DEFAULT_BRIDGE_DIRECTION
17 | ARG REACT_APP_ENABLED_BRIDGES
18 | ARG REACT_APP_INFURA_ID
19 | ARG REACT_APP_GAS_PRICE_FALLBACK_GWEI
20 | ARG REACT_APP_GAS_PRICE_SUPPLIER_URL
21 | ARG REACT_APP_GAS_PRICE_SPEED_TYPE
22 | ARG REACT_APP_GAS_PRICE_UPDATE_INTERVAL
23 | ARG REACT_APP_ETH_PRICE_API_URL
24 | ARG REACT_APP_ETH_PRICE_UPDATE_INTERVAL
25 | ARG REACT_APP_TITLE
26 | ARG REACT_APP_DESCRIPTION
27 | ARG REACT_APP_UI_STATUS_UPDATE_INTERVAL
28 | ARG REACT_APP_DEBUG_LOGS
29 | ARG REACT_APP_GRAPH_HEALTH_UPDATE_INTERVAL
30 | ARG REACT_APP_GRAPH_HEALTH_THRESHOLD_BLOCKS
31 | ARG REACT_APP_XDAI_RPC_URL
32 | ARG REACT_APP_POA_RPC_URL
33 | ARG REACT_APP_SOKOL_RPC_URL
34 | ARG REACT_APP_MAINNET_RPC_URL
35 | ARG REACT_APP_KOVAN_RPC_URL
36 | ARG REACT_APP_BSC_RPC_URL
37 | ARG REACT_APP_COINZILLA_API_KEY
38 | ARG REACT_APP_OWLRACLE_API_KEY
39 | ARG REACT_APP_SENTRY_DSN
40 |
41 | ENV REACT_APP_DEFAULT_BRIDGE_DIRECTION $REACT_APP_DEFAULT_BRIDGE_DIRECTION
42 | ENV REACT_APP_ENABLED_BRIDGES $REACT_APP_ENABLED_BRIDGES
43 | ENV REACT_APP_INFURA_ID $REACT_APP_INFURA_ID
44 | ENV REACT_APP_GAS_PRICE_FALLBACK_GWEI $REACT_APP_GAS_PRICE_FALLBACK_GWEI
45 | ENV REACT_APP_GAS_PRICE_SUPPLIER_URL $REACT_APP_GAS_PRICE_SUPPLIER_URL
46 | ENV REACT_APP_GAS_PRICE_SPEED_TYPE $REACT_APP_GAS_PRICE_SPEED_TYPE
47 | ENV REACT_APP_GAS_PRICE_UPDATE_INTERVAL $REACT_APP_GAS_PRICE_UPDATE_INTERVAL
48 | ENV REACT_APP_ETH_PRICE_API_URL $REACT_APP_ETH_PRICE_API_URL
49 | ENV REACT_APP_ETH_PRICE_UPDATE_INTERVAL $REACT_APP_ETH_PRICE_UPDATE_INTERVAL
50 | ENV REACT_APP_TITLE $REACT_APP_TITLE
51 | ENV REACT_APP_DESCRIPTION $REACT_APP_DESCRIPTION
52 | ENV REACT_APP_UI_STATUS_UPDATE_INTERVAL $REACT_APP_UI_STATUS_UPDATE_INTERVAL
53 | ENV REACT_APP_DEBUG_LOGS $REACT_APP_DEBUG_LOGS
54 | ENV REACT_APP_GRAPH_HEALTH_UPDATE_INTERVAL $REACT_APP_GRAPH_HEALTH_UPDATE_INTERVAL
55 | ENV REACT_APP_GRAPH_HEALTH_THRESHOLD_BLOCKS $REACT_APP_GRAPH_HEALTH_THRESHOLD_BLOCKS
56 | ENV REACT_APP_XDAI_RPC_URL $REACT_APP_XDAI_RPC_URL
57 | ENV REACT_APP_POA_RPC_URL $REACT_APP_POA_RPC_URL
58 | ENV REACT_APP_SOKOL_RPC_URL $REACT_APP_SOKOL_RPC_URL
59 | ENV REACT_APP_MAINNET_RPC_URL $REACT_APP_MAINNET_RPC_URL
60 | ENV REACT_APP_KOVAN_RPC_URL $REACT_APP_KOVAN_RPC_URL
61 | ENV REACT_APP_BSC_RPC_URL $REACT_APP_BSC_RPC_URL
62 | ENV REACT_APP_COINZILLA_API_KEY $REACT_APP_COINZILLA_API_KEY
63 | ENV REACT_APP_OWLRACLE_API_KEY $REACT_APP_OWLRACLE_API_KEY
64 | ENV REACT_APP_SENTRY_DSN $REACT_APP_SENTRY_DSN
65 |
66 | RUN yarn install --frozen-lockfile
67 |
68 | RUN yarn dapp:build
69 |
70 | # Stage 2 - the production environment
71 |
72 | FROM nginx:alpine
73 |
74 | COPY ./nginx.conf /etc/nginx/conf.d/default.conf
75 |
76 | WORKDIR /usr/share/nginx/html
77 |
78 | RUN rm -rf ./*
79 |
80 | COPY --from=build /usr/src/app/packages/dapp/build .
81 |
82 | EXPOSE 3000
83 |
84 | ENTRYPOINT ["nginx", "-g", "daemon off;"]
85 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Omnibridge
2 |
3 | The OmniBridge [multi-token extension](https://docs.tokenbridge.net/eth-xdai-amb-bridge/multi-token-extension) for the Arbitrary Message Bridge is the simplest way to transfer ANY ERC20/ERC677/ERC827 token to and from the xDai chain.
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### React App
10 |
11 | #### `yarn dapp:start`
12 |
13 | Runs the React app in development mode.
14 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
15 |
16 | The page will automatically reload if you make changes to the code.
17 | You will see the build errors and lint warnings in the console.
18 |
19 | #### `yarn dapp:test`
20 |
21 | Runs the React test watcher in an interactive mode.
22 | By default, runs tests related to files changed since the last commit.
23 |
24 | #### `yarn dapp:build`
25 |
26 | Builds the React app for production to the `build` folder.
27 | It correctly bundles React in production mode and optimizes the build for the best performance.
28 |
29 | The build is minified and the filenames include the hashes.
30 | Your app is ready to be deployed!
31 |
32 | ### Subgraph
33 |
34 | #### `yarn subgraph:auth`
35 |
36 | ```sh
37 | GRAPH_ACCESS_TOKEN=your-access-token-here yarn subgraph:auth
38 | ```
39 |
40 | #### `yarn subgraph:prepare-`
41 |
42 | Generates subgraph.yaml for particular network.
43 | Supported networks are kovan, sokol, xdai and mainnet.
44 |
45 | #### `yarn subgraph:codegen`
46 |
47 | Generates AssemblyScript types for smart contract ABIs and the subgraph schema.
48 |
49 | #### `yarn subgraph:build`
50 |
51 | Compiles the subgraph to WebAssembly.
52 |
53 | #### `yarn subgraph:deploy-`
54 |
55 | Deploys the subgraph for particular network to the official Graph Node.
56 |
57 | -
58 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 | services:
3 | dapp:
4 | build:
5 | context: .
6 | args:
7 | REACT_APP_DEFAULT_BRIDGE_DIRECTION: ${REACT_APP_DEFAULT_BRIDGE_DIRECTION}
8 | REACT_APP_ENABLED_BRIDGES: ${REACT_APP_ENABLED_BRIDGES}
9 | REACT_APP_INFURA_ID: ${REACT_APP_INFURA_ID}
10 | REACT_APP_GAS_PRICE_FALLBACK_GWEI: ${REACT_APP_GAS_PRICE_FALLBACK_GWEI}
11 | REACT_APP_GAS_PRICE_SUPPLIER_URL: ${REACT_APP_GAS_PRICE_SUPPLIER_URL}
12 | REACT_APP_GAS_PRICE_SPEED_TYPE: ${REACT_APP_GAS_PRICE_SPEED_TYPE}
13 | REACT_APP_GAS_PRICE_UPDATE_INTERVAL: ${REACT_APP_GAS_PRICE_UPDATE_INTERVAL}
14 | REACT_APP_ETH_PRICE_API_URL: ${REACT_APP_ETH_PRICE_API_URL}
15 | REACT_APP_ETH_PRICE_UPDATE_INTERVAL: ${REACT_APP_ETH_PRICE_UPDATE_INTERVAL}
16 | REACT_APP_TITLE: ${REACT_APP_TITLE}
17 | REACT_APP_DESCRIPTION: ${REACT_APP_DESCRIPTION}
18 | REACT_APP_UI_STATUS_UPDATE_INTERVAL: ${REACT_APP_UI_STATUS_UPDATE_INTERVAL}
19 | REACT_APP_DEBUG_LOGS: ${REACT_APP_DEBUG_LOGS}
20 | REACT_APP_GRAPH_HEALTH_UPDATE_INTERVAL: ${REACT_APP_GRAPH_HEALTH_UPDATE_INTERVAL}
21 | REACT_APP_GRAPH_HEALTH_THRESHOLD_BLOCKS: ${REACT_APP_GRAPH_HEALTH_THRESHOLD_BLOCKS}
22 | REACT_APP_XDAI_RPC_URL: ${REACT_APP_XDAI_RPC_URL}
23 | REACT_APP_POA_RPC_URL: ${REACT_APP_POA_RPC_URL}
24 | REACT_APP_SOKOL_RPC_URL: ${REACT_APP_SOKOL_RPC_URL}
25 | REACT_APP_MAINNET_RPC_URL: ${REACT_APP_MAINNET_RPC_URL}
26 | REACT_APP_KOVAN_RPC_URL: ${REACT_APP_KOVAN_RPC_URL}
27 | REACT_APP_BSC_RPC_URL: ${REACT_APP_BSC_RPC_URL}
28 | REACT_APP_COINZILLA_API_KEY: ${REACT_APP_COINZILLA_API_KEY}
29 | REACT_APP_OWLRACLE_API_KEY: ${REACT_APP_OWLRACLE_API_KEY}
30 | REACT_APP_SENTRY_DSN: ${REACT_APP_SENTRY_DSN}
31 | ports:
32 | - 3000:3000
33 |
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "hosting": {
3 | "public": "packages/dapp/build",
4 | "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
5 | "rewrites": [
6 | {
7 | "source": "**",
8 | "destination": "/index.html"
9 | }
10 | ],
11 | "headers": [
12 | {
13 | "source": "**/*.@(jpg|svg|png|json)",
14 | "headers": [
15 | {
16 | "key": "Access-Control-Allow-Origin",
17 | "value": "*"
18 | }
19 | ]
20 | }
21 | ]
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 |
3 | listen 3000;
4 |
5 | location / {
6 | # This would be the directory where your React app's static files are stored at
7 | root /usr/share/nginx/html;
8 | try_files $uri /index.html;
9 | }
10 |
11 | location ~* \.(jpg|svg|png|json)$ {
12 | root /usr/share/nginx/html;
13 | add_header Access-Control-Allow-Origin *;
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@omnibridge/ui",
3 | "version": "1.0.0",
4 | "keywords": [
5 | "ethereum",
6 | "react",
7 | "workspaces",
8 | "yarn"
9 | ],
10 | "private": true,
11 | "scripts": {
12 | "subgraph:auth": "yarn workspace @omnibridge/subgraph auth",
13 | "subgraph:codegen": "yarn workspace @omnibridge/subgraph codegen",
14 | "subgraph:build": "yarn workspace @omnibridge/subgraph build",
15 | "subgraph:prepare-kovan": "yarn workspace @omnibridge/subgraph prepare-kovan",
16 | "subgraph:deploy-kovan": "yarn workspace @omnibridge/subgraph deploy-kovan",
17 | "subgraph:prepare-sokol": "yarn workspace @omnibridge/subgraph prepare-sokol",
18 | "subgraph:deploy-sokol": "yarn workspace @omnibridge/subgraph deploy-sokol",
19 | "subgraph:prepare-xdai": "yarn workspace @omnibridge/subgraph prepare-xdai",
20 | "subgraph:deploy-xdai": "yarn workspace @omnibridge/subgraph deploy-xdai",
21 | "subgraph:prepare-mainnet": "yarn workspace @omnibridge/subgraph prepare-mainnet",
22 | "subgraph:deploy-mainnet": "yarn workspace @omnibridge/subgraph deploy-mainnet",
23 | "subgraph:prepare-xdai-bsc": "yarn workspace @omnibridge/subgraph prepare-xdai-bsc",
24 | "subgraph:deploy-xdai-bsc": "yarn workspace @omnibridge/subgraph deploy-xdai-bsc",
25 | "subgraph:prepare-bsc-xdai": "yarn workspace @omnibridge/subgraph prepare-bsc-xdai",
26 | "subgraph:deploy-bsc-xdai": "yarn workspace @omnibridge/subgraph deploy-bsc-xdai",
27 | "subgraph:prepare-mainnet-bsc": "yarn workspace @omnibridge/subgraph prepare-mainnet-bsc",
28 | "subgraph:deploy-mainnet-bsc": "yarn workspace @omnibridge/subgraph deploy-mainnet-bsc",
29 | "subgraph:prepare-bsc-mainnet": "yarn workspace @omnibridge/subgraph prepare-bsc-mainnet",
30 | "subgraph:deploy-bsc-mainnet": "yarn workspace @omnibridge/subgraph deploy-bsc-mainnet",
31 | "dapp:build": "yarn workspace @omnibridge/dapp build",
32 | "dapp:eject": "yarn workspace @omnibridge/dapp eject",
33 | "dapp:start": "yarn workspace @omnibridge/dapp start",
34 | "dapp:test": "yarn workspace @omnibridge/dapp test",
35 | "dapp:lint": "yarn workspace @omnibridge/dapp lint",
36 | "lint": "eslint --ignore-path .gitignore \"./packages/**/*.{ts,tsx,js,jsx}\"",
37 | "format": "prettier --ignore-path .gitignore --write \"{*,**/*}.{ts,tsx,js,jsx,json,md}\"",
38 | "prepare": "husky install"
39 | },
40 | "workspaces": {
41 | "nohoist": [
42 | "**/@graphprotocol/graph-ts",
43 | "**/@graphprotocol/graph-ts/**"
44 | ],
45 | "packages": [
46 | "packages/*"
47 | ]
48 | },
49 | "devDependencies": {
50 | "eslint-config-airbnb": "^19.0.4",
51 | "eslint-config-prettier": "^8.5.0",
52 | "eslint-plugin-import": "^2.26.0",
53 | "eslint-plugin-jsx-a11y": "^6.5.1",
54 | "eslint-plugin-react": "^7.29.4",
55 | "eslint-plugin-react-hooks": "^4.4.0",
56 | "eslint-plugin-simple-import-sort": "^7.0.0",
57 | "husky": "^7.0.4",
58 | "lint-staged": "^12.3.7",
59 | "prettier": "^2.6.2"
60 | },
61 | "license": "UNLICENSED",
62 | "lint-staged": {
63 | "*.{ts,tsx,js,jsx}": [
64 | "eslint --fix",
65 | "prettier --write"
66 | ],
67 | "*.{json,md}": "prettier --write"
68 | },
69 | "dependencies": {},
70 | "resolutions": {
71 | "react-error-overlay": "6.0.9"
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/packages/dapp/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["airbnb", "prettier", "react-app", "plugin:import/recommended"],
4 | "parserOptions": {
5 | "ecmaVersion": 6,
6 | "sourceType": "module"
7 | },
8 | "plugins": ["simple-import-sort"],
9 | "settings": {
10 | "import/resolver": {
11 | "node": {
12 | "extensions": [".js", ".jsx", ".d.ts", ".ts", ".tsx"],
13 | "moduleDirectory": ["node_modules", "src"]
14 | }
15 | }
16 | },
17 | "rules": {
18 | "import/no-default-export": "error",
19 | "import/prefer-default-export": "off",
20 | "import/no-relative-packages": "warn",
21 | "sort-imports": "off",
22 | "import/order": "off",
23 | "simple-import-sort/imports": "warn",
24 | "simple-import-sort/exports": "warn",
25 | "react/jsx-props-no-spreading": "off",
26 | "react/function-component-definition": "off",
27 | "react/prop-types": "off",
28 | "arrow-body-style": "warn"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/dapp/.sample-env:
--------------------------------------------------------------------------------
1 | REACT_APP_DEFAULT_BRIDGE_DIRECTION=kovan-sokol
2 | REACT_APP_ENABLED_BRIDGES=eth-xdai bsc-xdai poa-xdai eth-bsc kovan-sokol
3 | REACT_APP_INFURA_ID=
4 | REACT_APP_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
5 | REACT_APP_GAS_PRICE_SPEED_TYPE=standard
6 | REACT_APP_GAS_PRICE_FALLBACK_GWEI=50
7 | REACT_APP_GAS_PRICE_UPDATE_INTERVAL=60000
8 | REACT_APP_ETH_PRICE_API_URL=https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=USD
9 | REACT_APP_ETH_PRICE_UPDATE_INTERVAL=60000
10 | REACT_APP_TITLE=OmniBridge - %c
11 | REACT_APP_DESCRIPTION=The OmniBridge multi-token extension is the simplest way to transfer ANY ERC20/ERC677/ERC827 token to and from the xDai chain.
12 | REACT_APP_UI_STATUS_UPDATE_INTERVAL=5000
13 | REACT_APP_DEBUG_LOGS=true
14 | REACT_APP_GRAPH_HEALTH_UPDATE_INTERVAL=60000
15 | REACT_APP_GRAPH_HEALTH_THRESHOLD_BLOCKS=10
16 | REACT_APP_COINZILLA_API_KEY=
17 | REACT_APP_OWLRACLE_API_KEY=
18 | REACT_APP_SENTRY_DSN=
19 |
20 |
21 | REACT_APP_XDAI_RPC_URL=https://rpc.xdaichain.com https://dai.poa.network
22 | REACT_APP_POA_RPC_URL=https://core.poa.network
23 | REACT_APP_SOKOL_RPC_URL=https://sokol.poa.network
24 | REACT_APP_MAINNET_RPC_URL=https://mainnet.infura.io/v3/ https://mainnet-nethermind.blockscout.com/
25 | REACT_APP_KOVAN_RPC_URL=https://kovan.infura.io/v3/ https://kovan.poa.network/
26 | REACT_APP_BSC_RPC_URL=https://bsc-dataseed.binance.org https://bsc-dataseed1.defibit.io/
27 |
--------------------------------------------------------------------------------
/packages/dapp/.unimportedrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "ignoreUnresolved": [],
3 | "ignoreUnimported": [],
4 | "ignoreUnused": [],
5 | "ignorePatterns": [
6 | "**/node_modules/**",
7 | "**/*.stories.{js,jsx,ts,tsx}",
8 | "**/*.tests.{js,jsx,ts,tsx}",
9 | "**/*.test.{js,jsx,ts,tsx}",
10 | "**/*.spec.{js,jsx,ts,tsx}",
11 | "**/tests/**",
12 | "**/__tests__/**",
13 | "**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/packages/dapp/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "src",
4 | "include": ["src"]
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/dapp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@omnibridge/dapp",
3 | "version": "1.0.0",
4 | "browserslist": {
5 | "production": [
6 | ">0.2%",
7 | "not dead",
8 | "not op_mini all"
9 | ],
10 | "development": [
11 | "last 1 chrome version",
12 | "last 1 firefox version",
13 | "last 1 safari version"
14 | ]
15 | },
16 | "dependencies": {
17 | "@chakra-ui/icons": "^1.0.15",
18 | "@chakra-ui/react": "^1.8.7",
19 | "@chakra-ui/theme": "^1.10.0",
20 | "@davatar/react": "^1.8.1",
21 | "@emotion/react": "^11.9.0",
22 | "@emotion/styled": "^11.3.0",
23 | "@gnosis.pm/safe-apps-web3modal": "^12.0.0",
24 | "@sentry/react": "^6.19.7",
25 | "@sentry/tracing": "^6.19.7",
26 | "@uniswap/token-lists": "^1.0.0-beta.21",
27 | "@walletconnect/web3-provider": "^1.7.7",
28 | "ajv": "^6.12.6",
29 | "copy-to-clipboard": "^3.3.1",
30 | "ethers": "^5.4.4",
31 | "fast-memoize": "^2.5.2",
32 | "focus-visible": "^5.2.0",
33 | "framer-motion": "^4.1.17",
34 | "gas-price-oracle": "^0.3.3",
35 | "graphql": "^16.5.0",
36 | "graphql-request": "^3.5.0",
37 | "query-string": "^7.0.1",
38 | "react": "^17.0.2",
39 | "react-dom": "^17.0.2",
40 | "react-router-dom": "^5.2.0",
41 | "react-scripts": "^4.0.3",
42 | "walletlink": "^2.1.9",
43 | "web3modal": "^1.9.6"
44 | },
45 | "license": "UNLICENSED",
46 | "scripts": {
47 | "build": "GENERATE_SOURCEMAP=false react-scripts build",
48 | "eject": "react-scripts eject",
49 | "start": "react-scripts start",
50 | "test": "react-scripts test",
51 | "lint": "eslint \"./src/**/*.{js,jsx}\""
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/packages/dapp/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni/omnibridge-ui/ac183732f2b5ba0dddaaa919ecf56239907421d8/packages/dapp/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/packages/dapp/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni/omnibridge-ui/ac183732f2b5ba0dddaaa919ecf56239907421d8/packages/dapp/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/packages/dapp/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni/omnibridge-ui/ac183732f2b5ba0dddaaa919ecf56239907421d8/packages/dapp/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/packages/dapp/public/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #ffffff
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/dapp/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni/omnibridge-ui/ac183732f2b5ba0dddaaa919ecf56239907421d8/packages/dapp/public/favicon-16x16.png
--------------------------------------------------------------------------------
/packages/dapp/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni/omnibridge-ui/ac183732f2b5ba0dddaaa919ecf56239907421d8/packages/dapp/public/favicon-32x32.png
--------------------------------------------------------------------------------
/packages/dapp/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni/omnibridge-ui/ac183732f2b5ba0dddaaa919ecf56239907421d8/packages/dapp/public/favicon.ico
--------------------------------------------------------------------------------
/packages/dapp/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
20 |
26 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | OmniBridge
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/packages/dapp/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "OmniBridge",
3 | "name": "OmniBridge",
4 | "description": "The OmniBridge is the simplest way to transfer ANY ERC20/ERC677/ERC827 token to and from the xDai chain.",
5 | "iconPath": "android-chrome-512x512.png",
6 | "providedBy": { "name": "xDai Chain", "url": "https://xdaichain.com" },
7 | "icons": [
8 | {
9 | "src": "android-chrome-192x192.png",
10 | "sizes": "192x192",
11 | "type": "image/png"
12 | },
13 | {
14 | "src": "android-chrome-512x512.png",
15 | "sizes": "512x512",
16 | "type": "image/png"
17 | }
18 | ],
19 | "start_url": ".",
20 | "display": "standalone",
21 | "theme_color": "#EEF4FD",
22 | "background_color": "#EEF4FD"
23 | }
24 |
--------------------------------------------------------------------------------
/packages/dapp/public/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni/omnibridge-ui/ac183732f2b5ba0dddaaa919ecf56239907421d8/packages/dapp/public/mstile-150x150.png
--------------------------------------------------------------------------------
/packages/dapp/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/packages/dapp/public/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
27 |
--------------------------------------------------------------------------------
/packages/dapp/src/App.jsx:
--------------------------------------------------------------------------------
1 | import 'focus-visible/dist/focus-visible';
2 |
3 | import { ChakraProvider, CSSReset } from '@chakra-ui/react';
4 | import { css, Global } from '@emotion/react';
5 | import { ErrorBoundary } from '@sentry/react';
6 | import { ErrorFallback } from 'components/common/ErrorFallback';
7 | import { Layout } from 'components/common/Layout';
8 | import { Routes } from 'components/common/Routes';
9 | import { SettingsProvider } from 'contexts/SettingsContext';
10 | import { Web3Provider } from 'contexts/Web3Context';
11 | import { logError } from 'lib/helpers';
12 | import React from 'react';
13 | import { BrowserRouter as Router } from 'react-router-dom';
14 | import { theme } from 'theme';
15 |
16 | const GlobalStyles = css`
17 | /*
18 | This will hide the focus indicator if the element receives focus via the mouse,
19 | but it will still show up on keyboard focus.
20 | */
21 | .js-focus-visible :focus:not([data-focus-visible-added]) {
22 | outline: none;
23 | box-shadow: none;
24 | }
25 | `;
26 |
27 | export const App = () => (
28 |
29 |
30 |
31 | }
33 | onError={error => logError(error)}
34 | >
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | );
47 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/alert.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/blue-tick.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/bsc-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni/omnibridge-ui/ac183732f2b5ba0dddaaa919ecf56239907421d8/packages/dapp/src/assets/bsc-logo.png
--------------------------------------------------------------------------------
/packages/dapp/src/assets/change-network.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni/omnibridge-ui/ac183732f2b5ba0dddaaa919ecf56239907421d8/packages/dapp/src/assets/change-network.png
--------------------------------------------------------------------------------
/packages/dapp/src/assets/claim.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/close.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/coinbase.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/confirm-transfer.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/down-triangle.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/drop-down.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/error.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/eth-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni/omnibridge-ui/ac183732f2b5ba0dddaaa919ecf56239907421d8/packages/dapp/src/assets/eth-logo.png
--------------------------------------------------------------------------------
/packages/dapp/src/assets/eth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni/omnibridge-ui/ac183732f2b5ba0dddaaa919ecf56239907421d8/packages/dapp/src/assets/eth.png
--------------------------------------------------------------------------------
/packages/dapp/src/assets/imtoken.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/info.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/loading.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/metamask-fox.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/right-arrow.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/search.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/transfer.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/unlock.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/up-triangle.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/packages/dapp/src/assets/xdai-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni/omnibridge-ui/ac183732f2b5ba0dddaaa919ecf56239907421d8/packages/dapp/src/assets/xdai-logo.png
--------------------------------------------------------------------------------
/packages/dapp/src/components/bridge/ActionButtons.jsx:
--------------------------------------------------------------------------------
1 | import { Flex, Tooltip } from '@chakra-ui/react';
2 | import { TransferButton } from 'components/bridge/TransferButton';
3 | import { UnlockButton } from 'components/bridge/UnlockButton';
4 | import { useBridgeContext } from 'contexts/BridgeContext';
5 | import { useWeb3Context } from 'contexts/Web3Context';
6 | import { useApproval } from 'hooks/useApproval';
7 | import { getNetworkName } from 'lib/helpers';
8 | import React from 'react';
9 |
10 | export const ActionButtons = ({ tokenLimits }) => {
11 | const { fromToken, fromAmount, txHash } = useBridgeContext();
12 | const approval = useApproval(fromToken, fromAmount, txHash);
13 | const { isConnected, providerChainId, isSanctioned } = useWeb3Context();
14 |
15 | const isValid =
16 | !isSanctioned && isConnected && providerChainId === fromToken?.chainId;
17 |
18 | const inner = (
19 |
27 |
28 |
29 |
30 | );
31 |
32 | let tooltipTitle = '';
33 |
34 | if (!isValid) {
35 | if (isConnected) {
36 | if (isSanctioned) {
37 | tooltipTitle = 'Bridging is blocked';
38 | } else {
39 | tooltipTitle = `Please switch to ${getNetworkName(fromToken?.chainId)}`;
40 | }
41 | } else {
42 | tooltipTitle = 'Please connect your wallet';
43 | }
44 | }
45 |
46 | return isValid ? inner : {inner};
47 | };
48 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/bridge/AdvancedMenu.jsx:
--------------------------------------------------------------------------------
1 | import { CloseIcon } from '@chakra-ui/icons';
2 | import {
3 | Button,
4 | Flex,
5 | Input,
6 | InputGroup,
7 | InputRightElement,
8 | useDisclosure,
9 | } from '@chakra-ui/react';
10 | import { useBridgeContext } from 'contexts/BridgeContext';
11 | import { useWeb3Context } from 'contexts/Web3Context';
12 | import { utils } from 'ethers';
13 | import React, { useCallback } from 'react';
14 |
15 | export const AdvancedMenu = () => {
16 | const { isGnosisSafe } = useWeb3Context();
17 | const { isOpen, onOpen, onClose } = useDisclosure();
18 | const { receiver, setReceiver } = useBridgeContext();
19 |
20 | const isMenuOpen = isOpen || isGnosisSafe;
21 |
22 | const onClick = useCallback(() => {
23 | if (isMenuOpen) {
24 | setReceiver('');
25 | if (!isGnosisSafe) onClose();
26 | } else {
27 | onOpen();
28 | }
29 | }, [isMenuOpen, setReceiver, isGnosisSafe, onOpen, onClose]);
30 |
31 | return (
32 |
39 |
48 | {isMenuOpen ? (
49 |
50 | setReceiver(e.target.value)}
58 | isInvalid={!!receiver && !utils.isAddress(receiver)}
59 | _focus={{ boxShadow: '0 0 0 3px rgba(66, 153, 225, 0.6)' }}
60 | _invalid={{
61 | boxShadow: '0 0 0 3px #ef5d5d !important',
62 | }}
63 | />
64 |
65 |
73 |
74 |
75 | ) : (
76 |
87 | )}
88 |
89 |
90 | );
91 | };
92 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/bridge/SwitchButton.jsx:
--------------------------------------------------------------------------------
1 | import { IconButton, Tooltip } from '@chakra-ui/react';
2 | import { useBridgeContext } from 'contexts/BridgeContext';
3 | import { useWeb3Context } from 'contexts/Web3Context';
4 | import { useSwitchChain } from 'hooks/useSwitchChain';
5 | import { SwitchIcon } from 'icons/SwitchIcon';
6 | import React, { useCallback, useState } from 'react';
7 |
8 | export const SwitchButton = () => {
9 | const { isConnected, providerChainId, isMetamask } = useWeb3Context();
10 |
11 | const { switchTokens, toToken } = useBridgeContext();
12 | const switchChain = useSwitchChain();
13 |
14 | const isDefaultChain = [1, 3, 4, 5, 42].includes(toToken?.chainId);
15 | const isMobileBrowser = navigator?.userAgent?.includes('Mobile') ?? false;
16 | const canSwitchInWallet =
17 | isConnected && isMetamask && (isMobileBrowser ? !isDefaultChain : true);
18 |
19 | const [loading, setLoading] = useState(false);
20 |
21 | const switchOnClick = useCallback(
22 | () =>
23 | (async () => {
24 | setLoading(true);
25 | if (canSwitchInWallet && providerChainId !== toToken?.chainId) {
26 | await switchChain(toToken?.chainId);
27 | } else {
28 | switchTokens();
29 | }
30 | setLoading(false);
31 | })(),
32 | [switchChain, providerChainId, canSwitchInWallet, switchTokens, toToken],
33 | );
34 |
35 | return (
36 |
37 | }
39 | p="0.5rem"
40 | variant="ghost"
41 | position="absolute"
42 | top={{ base: '50%', lg: '0' }}
43 | left={{ base: '0', lg: '50%' }}
44 | transform={{
45 | base: 'translateY(-50%) translateX(0.5rem) rotate(90deg)',
46 | lg: 'translateX(-50%) translateY(calc(-100% - 1.25rem))',
47 | }}
48 | borderRadius="0.5rem"
49 | color="blue.500"
50 | _hover={{ bg: '#EEF4FD' }}
51 | onClick={switchOnClick}
52 | isLoading={loading}
53 | _loading={{
54 | bg: '#EEF4FD',
55 | }}
56 | />
57 |
58 | );
59 | };
60 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/AddToMetamask.jsx:
--------------------------------------------------------------------------------
1 | import { Image, useDisclosure, useToast } from '@chakra-ui/react';
2 | import MetamaskFox from 'assets/metamask-fox.svg';
3 | import { AddToMetamaskModal } from 'components/modals/AddToMetamaskModal';
4 | import { useWeb3Context } from 'contexts/Web3Context';
5 | import { ADDRESS_ZERO } from 'lib/constants';
6 | import { getNetworkName, logError } from 'lib/helpers';
7 | import { addTokenToMetamask } from 'lib/metamask';
8 | import React, { useCallback } from 'react';
9 |
10 | export const AddToMetamask = ({ token, asModal = false, ...props }) => {
11 | const { providerChainId, isMetamask } = useWeb3Context();
12 | const toast = useToast();
13 | const { isOpen, onOpen, onClose } = useDisclosure();
14 |
15 | const showError = useCallback(
16 | msg => {
17 | if (msg) {
18 | toast({
19 | title: 'Error: Unable to add token',
20 | description: msg,
21 | status: 'error',
22 | isClosable: 'true',
23 | });
24 | }
25 | },
26 | [toast],
27 | );
28 |
29 | const addToken = useCallback(async () => {
30 | if (providerChainId !== token.chainId) {
31 | showError(`Please switch wallet to ${getNetworkName(token.chainId)}`);
32 | } else {
33 | await addTokenToMetamask(token).catch(metamaskError => {
34 | logError({ metamaskError });
35 | if (metamaskError && metamaskError.message) {
36 | showError(
37 | `Please add the token ${token.address} manually in the wallet app. Got message: "${metamaskError.message}"`,
38 | );
39 | }
40 | });
41 | }
42 | }, [showError, token, providerChainId]);
43 |
44 | const onClick = useCallback(() => {
45 | if (asModal) {
46 | onOpen();
47 | } else {
48 | addToken();
49 | }
50 | }, [onOpen, asModal, addToken]);
51 |
52 | return isMetamask ? (
53 | <>
54 | {token.address !== ADDRESS_ZERO && (
55 |
63 | )}
64 | {asModal && isOpen && (
65 |
66 | )}
67 | >
68 | ) : null;
69 | };
70 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/BridgeDropdown.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | Menu,
4 | MenuButton,
5 | MenuItem,
6 | MenuList,
7 | Text,
8 | useBreakpointValue,
9 | } from '@chakra-ui/react';
10 | import { useSettings } from 'contexts/SettingsContext';
11 | import { DownArrowIcon } from 'icons/DownArrowIcon';
12 | import { NetworkIcon } from 'icons/NetworkIcon';
13 | import { networks } from 'lib/networks';
14 | import React, { useCallback, useEffect } from 'react';
15 |
16 | export const BridgeDropdown = ({ close }) => {
17 | const { bridgeDirection, setBridgeDirection } = useSettings();
18 | const placement = useBreakpointValue({ base: 'bottom', md: 'bottom-end' });
19 |
20 | const setItem = useCallback(
21 | e => {
22 | setBridgeDirection(e.target.value, true);
23 | close();
24 | },
25 | [close, setBridgeDirection],
26 | );
27 |
28 | const networkOptions = Object.keys(networks);
29 | const isValidNetwork = Object.keys(networks).indexOf(bridgeDirection) >= 0;
30 |
31 | const currentBridgeDirection = isValidNetwork
32 | ? bridgeDirection
33 | : networkOptions[0];
34 |
35 | useEffect(() => {
36 | if (!isValidNetwork) {
37 | setBridgeDirection(networkOptions[0], true);
38 | }
39 | }, [isValidNetwork, networkOptions, setBridgeDirection]);
40 |
41 | return (
42 |
72 | );
73 | };
74 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/CoinzillaBannerAd.jsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@chakra-ui/react';
2 | import { COINZILLA_API_KEY } from 'lib/constants';
3 | import React, { useEffect } from 'react';
4 |
5 | export const CoinzillaBannerAd = props => {
6 | useEffect(() => {
7 | window.coinzilla_display = window.coinzilla_display || [];
8 | window.coinzilla_display.push({
9 | zone: COINZILLA_API_KEY,
10 | width: window.screen.availWidth >= 728 ? '728' : '320',
11 | height: window.screen.availWidth >= 728 ? '90' : '100',
12 | });
13 | }, []);
14 |
15 | if (!COINZILLA_API_KEY) return null;
16 | return (
17 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/CoinzillaTextAd.jsx:
--------------------------------------------------------------------------------
1 | import { HStack, Image, Link, Text } from '@chakra-ui/react';
2 | import { useCoinzillaText } from 'hooks/useCoinzillaText';
3 | import React from 'react';
4 |
5 | export const CoinzillaTextAd = () => {
6 | const { isFetching, adData, adFetchError } = useCoinzillaText();
7 |
8 | if (isFetching || adFetchError || !adData) return null;
9 |
10 | const {
11 | ad: { thumbnail, name, title, url, cta_button: ctaButton },
12 | } = adData;
13 |
14 | return (
15 |
21 | Sponsored:
22 |
23 | {name}
24 | - {title}
25 |
26 | {ctaButton}
27 |
28 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/ErrorFallback.jsx:
--------------------------------------------------------------------------------
1 | import { Flex, Text } from '@chakra-ui/react';
2 | import React from 'react';
3 |
4 | export const ErrorFallback = () => (
5 |
12 | Something went wrong
13 | Please check console for error log
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/Footer.jsx:
--------------------------------------------------------------------------------
1 | import { Box, Flex, HStack, Text, useBreakpointValue } from '@chakra-ui/react';
2 | import { GithubIcon } from 'icons/GithubIcon';
3 | import { GnosisChainIcon } from 'icons/GnosisChainIcon';
4 | import { OmniBridgeIcon } from 'icons/OmniBridgeIcon';
5 | import { RaidGuildIcon } from 'icons/RaidGuildIcon';
6 | import { TelegramIcon } from 'icons/TelegramIcon';
7 | import { TwitterIcon } from 'icons/TwitterIcon';
8 | import React from 'react';
9 | import { Link } from 'react-router-dom';
10 |
11 | export const Footer = () => {
12 | const smallScreen = useBreakpointValue({ base: true, sm: false });
13 | return (
14 |
24 | {!smallScreen && (
25 |
26 |
32 |
33 |
34 |
35 | )}
36 |
37 |
38 |
43 |
44 |
45 |
46 |
47 |
52 |
53 |
54 |
55 |
56 |
61 |
62 |
63 |
64 |
65 |
70 |
71 |
72 |
73 |
74 |
79 |
84 | Built by
85 |
86 |
87 |
88 |
89 |
90 | );
91 | };
92 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/Layout.jsx:
--------------------------------------------------------------------------------
1 | import { Flex, Image } from '@chakra-ui/react';
2 | import DownTriangle from 'assets/down-triangle.svg';
3 | import UpTriangle from 'assets/up-triangle.svg';
4 | import { Footer } from 'components/common/Footer';
5 | import { TermsOfServiceModal } from 'components/modals/TermsOfServiceModal';
6 | import React from 'react';
7 |
8 | export const Layout = ({ children }) => (
9 |
21 |
29 |
37 |
46 | {children}
47 |
48 |
49 |
50 |
51 | );
52 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/Logo.jsx:
--------------------------------------------------------------------------------
1 | import { Image } from '@chakra-ui/react';
2 | import BSCLogo from 'assets/bsc-logo.png';
3 | import EthLogo from 'assets/eth-logo.png';
4 | import xDAILogo from 'assets/xdai-logo.png';
5 | import { uriToHttp } from 'lib/helpers';
6 | import React, { useState } from 'react';
7 |
8 | const BAD_SRCS = {};
9 |
10 | const logos = {
11 | 1: EthLogo,
12 | 42: EthLogo,
13 | 77: xDAILogo,
14 | 100: xDAILogo,
15 | 56: BSCLogo,
16 | };
17 |
18 | export const Logo = ({ uri, chainId }) => {
19 | const fallbackLogo = logos[chainId] ?? logos[1];
20 | const [, refresh] = useState(0);
21 |
22 | if (uri) {
23 | const srcs = uriToHttp(uri);
24 | const src = srcs.find(s => !BAD_SRCS[s]);
25 |
26 | if (src) {
27 | return (
28 | {
31 | if (src) BAD_SRCS[src] = true;
32 | refresh(i => i + 1);
33 | }}
34 | />
35 | );
36 | }
37 | }
38 |
39 | return ;
40 | };
41 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/ProgressRing.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const ProgressRing = ({ radius, stroke, progress, totalProgress }) => {
4 | const normalizedRadius = radius - stroke * 2;
5 | const circumference = normalizedRadius * 2 * Math.PI;
6 | const strokeDashoffset =
7 | circumference - (progress / totalProgress) * circumference;
8 |
9 | return (
10 |
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/Routes.jsx:
--------------------------------------------------------------------------------
1 | import { History } from 'pages/History';
2 | import { Home } from 'pages/Home';
3 | import React from 'react';
4 | import { Redirect, Route, Switch } from 'react-router-dom';
5 |
6 | export const Routes = () => (
7 |
8 |
9 |
10 |
11 |
12 | );
13 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/TxLink.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from '@chakra-ui/react';
2 | import { getExplorerUrl } from 'lib/helpers';
3 | import React from 'react';
4 |
5 | export const TxLink = ({ chainId, hash, children }) => {
6 | const link = `${getExplorerUrl(chainId)}/tx/${hash}`;
7 | if (hash)
8 | return (
9 |
18 | {children}
19 |
20 | );
21 | return children;
22 | };
23 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/WalletInfo.jsx:
--------------------------------------------------------------------------------
1 | import { Button, Flex, Text } from '@chakra-ui/react';
2 | import { useBridgeContext } from 'contexts/BridgeContext';
3 | import { useWeb3Context } from 'contexts/Web3Context';
4 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
5 | import { useRenderChain } from 'hooks/useRenderChain';
6 | import { WalletFilledIcon } from 'icons/WalletFilledIcon';
7 | import { getNetworkName } from 'lib/helpers';
8 | import React from 'react';
9 | import { useHistory } from 'react-router-dom';
10 |
11 | export const WalletInfo = ({ close }) => {
12 | const { homeChainId, foreignChainId, getBridgeChainId } =
13 | useBridgeDirection();
14 | const { fromToken } = useBridgeContext();
15 | const { isConnected, providerChainId, disconnect, loading } =
16 | useWeb3Context();
17 |
18 | const renderChain = useRenderChain();
19 | const {
20 | location: { pathname },
21 | } = useHistory();
22 |
23 | if (!isConnected) return null;
24 |
25 | const isHistoryPage = pathname === '/history';
26 |
27 | let isInvalid = false;
28 |
29 | if (isHistoryPage) {
30 | isInvalid = loading
31 | ? false
32 | : ![homeChainId, foreignChainId].includes(providerChainId);
33 | } else {
34 | isInvalid = loading ? false : providerChainId !== fromToken?.chainId;
35 | }
36 |
37 | return (
38 |
39 |
48 |
49 |
50 |
51 | {isInvalid
52 | ? 'Switch your network'
53 | : `Connected to ${getNetworkName(providerChainId)}`}
54 |
55 | {isInvalid && (
56 |
57 | {isHistoryPage ? (
58 | <>
59 | Please switch to {renderChain(foreignChainId)} for claiming your
60 | tokens
61 | >
62 | ) : (
63 | <>
64 | Please switch to {renderChain(fromToken?.chainId)} for bridging
65 | your tokens to{' '}
66 | {getNetworkName(getBridgeChainId(fromToken?.chainId))}
67 | >
68 | )}
69 |
70 | )}
71 |
72 |
82 |
83 | );
84 | };
85 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/common/WalletSelector.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | Flex,
4 | Popover,
5 | PopoverBody,
6 | PopoverContent,
7 | PopoverTrigger,
8 | Text,
9 | useBreakpointValue,
10 | } from '@chakra-ui/react';
11 | import Davatar from '@davatar/react';
12 | import { useBridgeContext } from 'contexts/BridgeContext';
13 | import { useWeb3Context } from 'contexts/Web3Context';
14 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
15 | import { useENS } from 'hooks/useENS';
16 | import { WalletIcon } from 'icons/WalletIcon';
17 | import { getAccountString, getNetworkLabel } from 'lib/helpers';
18 | import React from 'react';
19 | import { useHistory } from 'react-router-dom';
20 |
21 | import { WalletInfo } from './WalletInfo';
22 |
23 | export const WalletSelector = ({ close }) => {
24 | const { account, loading, providerChainId, connectWeb3 } = useWeb3Context();
25 | const { ensName } = useENS(account);
26 | const { fromToken } = useBridgeContext();
27 | const { homeChainId, foreignChainId } = useBridgeDirection();
28 | const {
29 | location: { pathname },
30 | } = useHistory();
31 |
32 | let isInvalid = false;
33 |
34 | if (pathname === '/history') {
35 | isInvalid = loading
36 | ? false
37 | : ![homeChainId, foreignChainId].includes(providerChainId);
38 | } else {
39 | isInvalid =
40 | loading || !fromToken ? false : providerChainId !== fromToken?.chainId;
41 | }
42 |
43 | const placement = useBreakpointValue({ base: 'bottom', md: 'bottom-end' });
44 | if (!account || !providerChainId)
45 | return (
46 | }
50 | >
51 | Connect Wallet
52 |
53 | );
54 | return (
55 |
56 |
57 |
58 |
82 |
83 |
84 |
90 |
91 |
92 |
93 |
94 |
95 | );
96 | };
97 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/history/HistoryPagination.jsx:
--------------------------------------------------------------------------------
1 | import { Button, Flex, HStack } from '@chakra-ui/react';
2 | import { LeftIcon } from 'icons/LeftIcon';
3 | import { RightIcon } from 'icons/RightIcon';
4 | import React, { useEffect } from 'react';
5 | import { useHistory } from 'react-router-dom';
6 |
7 | const getDisplayPages = (total, current) => {
8 | if (total === current) return [current - 2, current - 1, current];
9 | if (current === 1) return [current, current + 1, current + 2];
10 | return [current - 1, current, current + 1];
11 | };
12 |
13 | export const HistoryPagination = ({ numPages, currentPage }) => {
14 | const history = useHistory();
15 |
16 | useEffect(() => {
17 | const unlisten = history.listen(() => {
18 | window.scrollTo({ top: 0, left: 0 });
19 | });
20 | return () => {
21 | unlisten();
22 | };
23 | }, [history]);
24 |
25 | const onClick = page => {
26 | history.push(`/history?page=${page}`);
27 | };
28 |
29 | const displayPages = getDisplayPages(numPages, currentPage);
30 |
31 | return (
32 |
40 |
41 | onClick(currentPage - 1)}
46 | cursor={currentPage > 1 ? 'pointer' : 'not-allowed'}
47 | />
48 | {displayPages.map(
49 | page =>
50 | page >= 1 &&
51 | page <= numPages && (
52 |
62 | ),
63 | )}
64 | onClick(currentPage + 1)}
69 | cursor={currentPage < numPages ? 'pointer' : 'not-allowed'}
70 | />
71 |
72 |
73 | );
74 | };
75 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/history/ManualClaim.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | Flex,
4 | Input,
5 | InputGroup,
6 | InputRightElement,
7 | Text,
8 | useToast,
9 | } from '@chakra-ui/react';
10 | import { useWeb3Context } from 'contexts/Web3Context';
11 | import { useClaim } from 'hooks/useClaim';
12 | import { isRevertedError, TOKENS_CLAIMED } from 'lib/amb';
13 | import { handleWalletError, logError } from 'lib/helpers';
14 | import React, { useCallback, useState } from 'react';
15 |
16 | export const ManualClaim = ({ handleClaimError }) => {
17 | const { isConnected } = useWeb3Context();
18 | const [txHash, setTxHash] = useState('');
19 | const [loading, setLoading] = useState(false);
20 | const { claim, executing } = useClaim();
21 |
22 | const toast = useToast();
23 | const showError = useCallback(
24 | msg => {
25 | if (msg) {
26 | toast({
27 | title: 'Error',
28 | description: msg,
29 | status: 'error',
30 | isClosable: 'true',
31 | });
32 | }
33 | },
34 | [toast],
35 | );
36 |
37 | const claimTokens = useCallback(async () => {
38 | if (!txHash) return;
39 | setLoading(true);
40 | try {
41 | await claim(txHash);
42 | setTxHash('');
43 | } catch (manualClaimError) {
44 | logError({ manualClaimError });
45 | if (
46 | manualClaimError?.message === TOKENS_CLAIMED ||
47 | isRevertedError(manualClaimError)
48 | ) {
49 | setTxHash('');
50 | handleClaimError();
51 | } else {
52 | handleWalletError(manualClaimError, showError);
53 | }
54 | } finally {
55 | setLoading(false);
56 | }
57 | }, [claim, txHash, showError, handleClaimError]);
58 |
59 | if (!isConnected) return null;
60 |
61 | return (
62 |
73 |
80 |
81 | Can't find your transfer to claim tokens?
82 |
83 |
84 | Enter the transaction hash where the token transfer happened{' '}
85 |
86 |
87 |
88 | setTxHash(e.target.value)}
95 | pr="6rem"
96 | />
97 |
98 |
108 |
109 |
110 |
111 | );
112 | };
113 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/history/NoHistory.jsx:
--------------------------------------------------------------------------------
1 | import { Box, Button, Flex, Grid, Image, Text } from '@chakra-ui/react';
2 | import NoHistoryImage from 'assets/no-history.svg';
3 | import React from 'react';
4 | import { Link } from 'react-router-dom';
5 |
6 | export const NoHistory = () => (
7 |
16 |
17 |
27 |
33 |
34 |
35 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
53 | No History Found
54 |
55 |
56 |
59 |
60 |
61 | );
62 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/modals/ClaimErrorModal.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | Button,
4 | Flex,
5 | Image,
6 | Modal,
7 | ModalBody,
8 | ModalCloseButton,
9 | ModalContent,
10 | ModalFooter,
11 | ModalHeader,
12 | ModalOverlay,
13 | Text,
14 | } from '@chakra-ui/react';
15 | import BlueTickImage from 'assets/blue-tick.svg';
16 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
17 | import { getNetworkName } from 'lib/helpers';
18 | import React from 'react';
19 |
20 | const ClaimErrorModal = ({ onClose, claimErrorShow, claimErrorToken }) => {
21 | const { foreignChainId } = useBridgeDirection();
22 | return (
23 |
24 |
25 |
31 |
32 | Transfer done already
33 |
34 |
35 |
42 |
43 |
44 |
45 |
46 | The tokens were already claimed. Check your
47 | {claimErrorToken ? ` ${claimErrorToken.symbol} ` : ' '}
48 | token balance in{' '}
49 | {getNetworkName(foreignChainId)}.
50 |
51 |
52 |
53 |
54 |
55 |
61 |
70 |
71 |
72 |
73 |
74 |
75 | );
76 | };
77 |
78 | export { ClaimErrorModal };
79 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/modals/ConfirmBSCTokenModal.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Alert,
3 | AlertIcon,
4 | Button,
5 | Checkbox,
6 | Flex,
7 | Modal,
8 | ModalBody,
9 | ModalContent,
10 | ModalFooter,
11 | ModalHeader,
12 | ModalOverlay,
13 | Text,
14 | } from '@chakra-ui/react';
15 | import { utils } from 'ethers';
16 | import { getNetworkName } from 'lib/helpers';
17 | import React, { useCallback, useState } from 'react';
18 |
19 | const storageKey = token =>
20 | `${utils.hexlify(
21 | token.chainId,
22 | )}-${token.address.toLowerCase()}-confirm-eth-bsc`;
23 |
24 | export const shouldShowBSCTokenModal = token =>
25 | window.localStorage.getItem(storageKey(token)) !== 'false';
26 |
27 | const notShowBSCTokenModal = token =>
28 | window.localStorage.setItem(storageKey(token), 'false');
29 |
30 | export const ConfirmBSCTokenModal = ({
31 | isOpen,
32 | onClose,
33 | onConfirm,
34 | token,
35 | bridgeChainId,
36 | }) => {
37 | const [isChecked, setChecked] = useState(false);
38 |
39 | const onClick = useCallback(() => {
40 | if (isChecked) {
41 | notShowBSCTokenModal(token);
42 | }
43 | onClose();
44 | onConfirm();
45 | }, [isChecked, onClose, onConfirm, token]);
46 |
47 | return (
48 |
55 |
56 |
62 |
63 |
64 | Bridging {token?.name || token?.symbol || 'token'} to{' '}
65 | {getNetworkName(bridgeChainId)}
66 |
67 |
68 |
69 |
70 |
71 |
72 | Make sure that the bridged token is compatible with the token
73 | which already exists on the target chain.
74 |
75 |
76 | setChecked(e.target.checked)}
80 | borderColor="grey"
81 | borderRadius="4px"
82 | size="lg"
83 | variant="solid"
84 | mb="1rem"
85 | >
86 |
87 | Don't display this window for this token again
88 |
89 |
90 |
91 |
92 |
93 |
101 |
102 |
103 |
104 |
105 |
106 | );
107 | };
108 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/modals/LoadingModal.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Flex,
3 | Image,
4 | Link,
5 | Modal,
6 | ModalBody,
7 | ModalContent,
8 | ModalOverlay,
9 | Spinner,
10 | Text,
11 | } from '@chakra-ui/react';
12 | import LoadingImage from 'assets/loading.svg';
13 | import { useWeb3Context } from 'contexts/Web3Context';
14 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
15 | import React from 'react';
16 |
17 | const getTransactionString = hash => {
18 | if (!hash) return 'here';
19 | const len = hash.length;
20 | return `${hash.substr(0, 6)}...${hash.substr(len - 4, len - 1)}`;
21 | };
22 |
23 | export const LoadingModal = ({ loadingText, txHash, chainId }) => {
24 | const { getMonitorUrl } = useBridgeDirection();
25 | const { providerChainId } = useWeb3Context();
26 | return (
27 |
28 |
29 | {loadingText ? (
30 |
36 |
37 |
41 |
52 |
60 |
61 |
68 | {`${loadingText}...`}
69 |
70 | {'Monitor at ALM '}
71 |
77 | {getTransactionString(txHash)}
78 |
79 |
80 |
81 |
82 |
83 |
84 | ) : (
85 |
86 |
87 |
88 |
89 | Loading ...
90 |
91 |
92 |
93 | )}
94 |
95 |
96 | );
97 | };
98 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/modals/NeedsConfirmationModal.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Alert,
3 | AlertIcon,
4 | Box,
5 | Flex,
6 | Image,
7 | Modal,
8 | ModalBody,
9 | ModalCloseButton,
10 | ModalContent,
11 | ModalHeader,
12 | ModalOverlay,
13 | Text,
14 | } from '@chakra-ui/react';
15 | import ChangeNetworkImage from 'assets/change-network.png';
16 | import { useBridgeContext } from 'contexts/BridgeContext';
17 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
18 | import { useRenderChain } from 'hooks/useRenderChain';
19 | import { getNetworkName } from 'lib/helpers';
20 | import React, { useState } from 'react';
21 |
22 | export const NeedsConfirmationModal = ({
23 | setNeedsConfirmation,
24 | setMessage,
25 | }) => {
26 | const { foreignChainId } = useBridgeDirection();
27 | const { fromToken, toToken, setTxHash } = useBridgeContext();
28 | const toUnit =
29 | (toToken !== undefined && toToken.symbol) ||
30 | (fromToken !== undefined && fromToken.symbol);
31 |
32 | const [isOpen, setOpen] = useState(true);
33 | const onClose = () => {
34 | setNeedsConfirmation(false);
35 | setTxHash();
36 | setMessage();
37 | setOpen(false);
38 | };
39 |
40 | const renderChain = useRenderChain();
41 |
42 | return (
43 |
44 |
45 |
51 |
52 | Claim Your Tokens
53 |
54 |
55 |
62 |
63 |
64 |
65 |
66 | Please switch the network in your wallet to
67 |
68 | {renderChain(foreignChainId)}
69 |
70 |
71 |
72 |
73 |
74 | After you switch networks, you will complete a second
75 | transaction on {getNetworkName(foreignChainId)} to claim
76 | your {toUnit} tokens.
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | );
86 | };
87 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/modals/SelectTokenModal.jsx:
--------------------------------------------------------------------------------
1 | import { CustomTokenModal } from 'components/modals/CustomTokenModal';
2 | import { TokenListModal } from 'components/modals/TokenListModal';
3 | import React, { useState } from 'react';
4 |
5 | export const SelectTokenModal = ({ isOpen, onClose }) => {
6 | const [custom, setCustom] = useState(false);
7 |
8 | return (
9 | <>
10 | setCustom(true)}
14 | />
15 | {
18 | setCustom(false);
19 | onClose();
20 | }}
21 | onBack={() => setCustom(false)}
22 | />
23 | >
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/AuspiciousGasWarning.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Flex, Text } from '@chakra-ui/react';
2 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
3 | import { useGasPrice } from 'hooks/useGasPrice';
4 | import React from 'react';
5 |
6 | export const AuspiciousGasWarning = ({
7 | noShadow = false,
8 | noMargin = false,
9 | }) => {
10 | const { foreignChainId } = useBridgeDirection();
11 | const { currentPrice, medianPrice, lowestPrice } = useGasPrice();
12 |
13 | if (
14 | foreignChainId === 1 &&
15 | lowestPrice.gt(0) &&
16 | medianPrice.gt(0) &&
17 | medianPrice.gt(currentPrice)
18 | ) {
19 | const medianPercent = medianPrice
20 | .sub(currentPrice)
21 | .mul(100)
22 | .div(medianPrice)
23 | .toNumber();
24 |
25 | const lowestPercent = currentPrice
26 | .sub(lowestPrice)
27 | .mul(100)
28 | .div(currentPrice)
29 | .toNumber();
30 |
31 | let msg = `${medianPercent}% below the median`;
32 |
33 | if (lowestPrice.gte(currentPrice)) {
34 | msg = 'the lowest';
35 | } else if (currentPrice.lte(medianPrice.sub(medianPrice.div(4)))) {
36 | msg = `${lowestPercent}% above the lowest`;
37 | }
38 |
39 | return (
40 |
46 |
53 |
54 |
55 | {`The current gas price on the Ethereum Mainnet is `}
56 | {msg}
57 | {` for the past 7 days`}
58 |
59 |
60 |
61 | );
62 | }
63 | return null;
64 | };
65 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/BSCETHTokenWarnings.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Flex, Text } from '@chakra-ui/react';
2 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
3 | import { ETH_BSC_BRIDGE } from 'lib/constants';
4 | import React from 'react';
5 |
6 | const BSC_ASSETS = [
7 | '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d'.toLowerCase(), // USDC
8 | '0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3'.toLowerCase(), // DAI
9 | '0x55d398326f99059ff775485246999027b3197955'.toLowerCase(), // USDT
10 | '0x2170ed0880ac9a755fd29b2688956bd959f933f8'.toLowerCase(), // ETH
11 | '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c'.toLowerCase(), // WBNB
12 | ];
13 |
14 | const ETH_ASSETS = [
15 | '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'.toLowerCase(), // USDC
16 | '0x6b175474e89094c44da98b954eedeac495271d0f'.toLowerCase(), // DAI
17 | '0xdac17f958d2ee523a2206206994597c13d831ec7'.toLowerCase(), // USDT
18 | '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'.toLowerCase(), // WETH
19 | '0xB8c77482e45F1F44dE1745F52C74426C631bDD52'.toLowerCase(), // BNB
20 | ];
21 |
22 | const isBSCAsset = token => {
23 | if (!token) return false;
24 | const { address, chainId } = token;
25 | return chainId === 56 && BSC_ASSETS.includes(address.toLowerCase());
26 | };
27 |
28 | const isETHAsset = token => {
29 | if (!token) return false;
30 | const { address, chainId } = token;
31 | return chainId === 1 && ETH_ASSETS.includes(address.toLowerCase());
32 | };
33 |
34 | export const BSCETHTokenWarnings = ({ token, noShadow = false }) => {
35 | const { bridgeDirection } = useBridgeDirection();
36 | if (bridgeDirection !== ETH_BSC_BRIDGE) return null;
37 | const isETHToken = isETHAsset(token);
38 | const isBSCToken = isBSCAsset(token);
39 |
40 | if (isETHToken) {
41 | return (
42 |
43 |
50 |
51 |
52 | Bridging this token DOES NOT mint the Binance-Peg token on the
53 | Binance Smart Chain.
54 |
55 |
56 |
57 | );
58 | }
59 | if (isBSCToken) {
60 | return (
61 |
62 |
69 |
70 |
71 | Bridging this Binance-Peg token to the ETH mainnet DOES NOT mint the
72 | official ERC20 token.
73 |
74 |
75 |
76 | );
77 | }
78 | return null;
79 | };
80 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/DaiWarning.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Flex, Link, Text } from '@chakra-ui/react';
2 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
3 | import { ETH_XDAI_BRIDGE } from 'lib/constants';
4 | import React from 'react';
5 |
6 | const ERC20_DAI_ADDRESS =
7 | '0x6B175474E89094C44Da98b954EedeAC495271d0F'.toLowerCase();
8 |
9 | export const isERC20DaiAddress = token => {
10 | if (!token) return false;
11 |
12 | const { chainId, address } = token;
13 | return chainId === 1 && address.toLowerCase() === ERC20_DAI_ADDRESS;
14 | };
15 |
16 | const XDaiBridgeLink = () => (
17 |
18 | xDai Ethereum Bridge
19 |
20 | );
21 |
22 | export const DaiWarning = ({ token, noShadow = false }) => {
23 | const { bridgeDirection } = useBridgeDirection();
24 | const isERC20Dai =
25 | bridgeDirection === ETH_XDAI_BRIDGE && isERC20DaiAddress(token);
26 |
27 | return isERC20Dai ? (
28 |
29 |
34 |
35 |
36 | Bridging DAI token to Gnosis Chain DOES NOT mint native xDai token. If
37 | you want native xDai, use the .
38 |
39 |
40 |
41 | ) : null;
42 | };
43 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/GCOriginOnBSCTokenWarning.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Flex, Text } from '@chakra-ui/react';
2 | import { useTokenGCOriginOnBSC } from 'hooks/useTokenDisabled';
3 | import React from 'react';
4 |
5 | export const GCOriginOnBSCTokenWarning = ({ token, noShadow = false }) => {
6 | const { isToken } = useTokenGCOriginOnBSC(token);
7 |
8 | if (!token || !isToken) return null;
9 |
10 | return (
11 |
12 |
17 |
18 |
19 | Bridging of tokens initially bridged from the Gnosis Chain is not
20 | allowed in this direction. Bridge them back to the Gnosis Chain and
21 | then to the Ethereum Mainnet.
22 |
23 |
24 |
25 | );
26 | };
27 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/GnosisSafeWarning.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Checkbox, Flex, Text } from '@chakra-ui/react';
2 | import { useBridgeContext } from 'contexts/BridgeContext';
3 | import { useWeb3Context } from 'contexts/Web3Context';
4 | import React from 'react';
5 |
6 | export const GnosisSafeWarning = ({
7 | isChecked,
8 | setChecked,
9 | noShadow = false,
10 | noCheckbox = false,
11 | }) => {
12 | const { isGnosisSafe, account } = useWeb3Context();
13 | const { receiver } = useBridgeContext();
14 |
15 | if (!isGnosisSafe) return null;
16 | const isSameAddress =
17 | account && receiver && account.toLowerCase() === receiver.toLowerCase();
18 | return (
19 |
20 | {(noCheckbox || isSameAddress) && (
21 |
29 |
30 | {noCheckbox && (
31 |
32 | It is mandatory to set an alternative recipient address when
33 | Omnibridge is loaded as a Gnosis Safe App. Usually this would be
34 | the address of a Gnosis Safe on the other side of the bridge.
35 |
36 | )}
37 | {isSameAddress && !noCheckbox && (
38 |
39 | You have specified the same address as the current Gnosis Safe
40 | wallet address.
41 |
42 | )}
43 |
44 | )}
45 | {!noCheckbox && isSameAddress && (
46 | setChecked(e.target.checked)}
50 | borderColor="grey"
51 | borderRadius="4px"
52 | size="lg"
53 | variant="solid"
54 | >
55 |
56 | I agree to proceed and understand I will receive the funds on the
57 | same address on the other side of the bridge.
58 |
59 |
60 | )}
61 |
62 | );
63 | };
64 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/GraphHealthWarning.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Flex, Text } from '@chakra-ui/react';
2 | import { useGraphHealth } from 'hooks/useGraphHealth';
3 | import React from 'react';
4 |
5 | export const GraphHealthWarning = () => {
6 | const { foreignHealthy, homeHealthy } = useGraphHealth();
7 | if (foreignHealthy && homeHealthy) return null;
8 |
9 | return (
10 |
11 |
16 |
17 |
18 | The Graph service may not work properly and some transfers may not
19 | display. You can use the form below to claim your tokens. If your
20 | transfer is still displayed as unclaimed double check its status in
21 | AMB Live Monitoring app by clicking the link in the Sending Tx column.
22 |
23 |
24 |
25 | );
26 | };
27 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/InflationaryTokenWarning.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Checkbox, Flex, Link, Text } from '@chakra-ui/react';
2 | import React from 'react';
3 |
4 | const ETH_INFLATIONARY_TOKENS = [
5 | '0xae7ab96520de3a18e5e111b5eaab095312d7fe84'.toLowerCase(),
6 | '0xdfe66b14d37c77f4e9b180ceb433d1b164f0281d'.toLowerCase(),
7 | '0xe95a203b1a91a908f9b9ce46459d101078c2c3cb'.toLowerCase(),
8 | '0xcbc1065255cbc3ab41a6868c22d1f1c573ab89fd'.toLowerCase(),
9 | ];
10 |
11 | const BSC_INFLATIONARY_TOKENS = [
12 | '0x250632378e573c6be1ac2f97fcdf00515d0aa91b'.toLowerCase(),
13 | ];
14 |
15 | export const isInflationaryToken = token => {
16 | if (!token) return false;
17 | const { chainId, address } = token;
18 | switch (chainId) {
19 | case 56:
20 | return BSC_INFLATIONARY_TOKENS.includes(address.toLowerCase());
21 | case 1:
22 | return ETH_INFLATIONARY_TOKENS.includes(address.toLowerCase());
23 | case 100:
24 | case 77:
25 | case 42:
26 | default:
27 | return false;
28 | }
29 | };
30 |
31 | const ExceptionsLink = ({ msg }) => (
32 |
37 | {msg}
38 |
39 | );
40 |
41 | export const InflationaryTokenWarning = ({
42 | token,
43 | isChecked,
44 | setChecked,
45 | noShadow = false,
46 | noCheckbox = false,
47 | }) => (
48 |
49 |
55 |
56 |
57 | {token.symbol} is an inflationary token. Any accumulated gains WILL NOT
58 | be added to your balance upon exit. Please see{' '}
59 | before bridging.
60 |
61 |
62 | {!noCheckbox && (
63 | setChecked(e.target.checked)}
67 | borderColor="grey"
68 | borderRadius="4px"
69 | size="lg"
70 | variant="solid"
71 | >
72 |
73 | I agree to proceed and understand I will not receive inflation.
74 |
75 |
76 | )}
77 |
78 | );
79 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/MedianGasWarning.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Flex, Text } from '@chakra-ui/react';
2 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
3 | import { useGasPrice } from 'hooks/useGasPrice';
4 | import React from 'react';
5 |
6 | export const MedianGasWarning = ({ noShadow = false }) => {
7 | const { foreignChainId } = useBridgeDirection();
8 | const { currentPrice, medianPrice } = useGasPrice();
9 |
10 | if (
11 | foreignChainId === 1 &&
12 | medianPrice.gt(0) &&
13 | medianPrice.lt(currentPrice)
14 | ) {
15 | const percent = currentPrice
16 | .sub(medianPrice)
17 | .mul(100)
18 | .div(medianPrice)
19 | .toNumber();
20 |
21 | return (
22 |
23 |
30 |
31 |
32 | {`The current gas price on the Ethereum Mainnet is `}
33 | {`${percent}% above the median`}
34 | {` for the past 7 days`}
35 |
36 |
37 |
38 | );
39 | }
40 | return null;
41 | };
42 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/NeedsTransactionsWarning.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Flex, Link, Text } from '@chakra-ui/react';
2 | import { utils } from 'ethers';
3 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
4 | import { useETHPrice } from 'hooks/useETHPrice';
5 | import { useGasPrice } from 'hooks/useGasPrice';
6 | import { networkCurrencies } from 'lib/constants';
7 | import { getNetworkName } from 'lib/helpers';
8 | import React from 'react';
9 |
10 | const LearnMoreLink = () => (
11 |
16 | learn more
17 |
18 | );
19 |
20 | export const NeedsTransactionsWarning = ({ noShadow = false }) => {
21 | const { homeChainId, foreignChainId } = useBridgeDirection();
22 | const GAS_COST = 260000;
23 |
24 | const { currentPrice: gasPrice } = useGasPrice();
25 | const ethPrice = useETHPrice();
26 | const gasCostInETH = utils.formatEther(gasPrice.mul(GAS_COST));
27 | const gasCostInUSD = gasCostInETH * ethPrice;
28 |
29 | const gasCostInETHString = Number(gasCostInETH).toFixed(3);
30 | const gasCostInUSDString = gasCostInUSD.toFixed(2);
31 |
32 | let txCostText = '';
33 | if (foreignChainId === 1) {
34 | txCostText = (
35 | <>
36 | You will need some {networkCurrencies[homeChainId].symbol} and
37 | approximately {gasCostInETHString} ETH ({gasCostInUSDString} USD) to
38 | complete. When claiming, your wallet may show a higher, less accurate
39 | estimate (
40 | ).
41 | >
42 | );
43 | }
44 |
45 | return (
46 |
47 |
52 |
53 |
54 | {`The transfer process requires 2 transactions, one on ${getNetworkName(
55 | homeChainId,
56 | )} and one
57 | on ${getNetworkName(foreignChainId)}. `}
58 | {txCostText}
59 |
60 |
61 |
62 | );
63 | };
64 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/RPCHealthWarning.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Flex, Text } from '@chakra-ui/react';
2 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
3 | import { useRPCHealth } from 'hooks/useRPCHealth';
4 | import { getNetworkName } from 'lib/helpers';
5 | import React from 'react';
6 |
7 | export const RPCHealthWarning = () => {
8 | const { foreignHealthy, homeHealthy } = useRPCHealth();
9 | const { foreignChainId, homeChainId } = useBridgeDirection();
10 |
11 | if (foreignHealthy && homeHealthy) return null;
12 |
13 | const bothSides = !homeHealthy && !foreignHealthy;
14 |
15 | return (
16 |
17 |
22 |
23 |
24 | {`The ${!homeHealthy ? getNetworkName(homeChainId) : ''} ${
25 | bothSides ? 'and' : ''
26 | } ${!foreignHealthy ? getNetworkName(foreignChainId) : ''} RPC-node${
27 | bothSides ? 's are' : ' is'
28 | } not responding. Please set custom RPC URL in settings or come back later.`}
29 |
30 |
31 |
32 | );
33 | };
34 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/RebasingTokenWarning.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Flex, Link, Text } from '@chakra-ui/react';
2 | import React from 'react';
3 |
4 | const ETH_REBASING_TOKENS = [
5 | '0xd46ba6d942050d489dbd938a2c909a5d5039a161'.toLowerCase(),
6 | '0x798d1be841a82a273720ce31c822c61a67a601c3'.toLowerCase(),
7 | '0x1c7bbadc81e18f7177a95eb1593e5f5f35861b10'.toLowerCase(),
8 | '0x67c597624b17b16fb77959217360b7cd18284253'.toLowerCase(),
9 | '0x68a118ef45063051eac49c7e647ce5ace48a68a5'.toLowerCase(),
10 | '0x68a118ef45063051eac49c7e647ce5ace48a68a5'.toLowerCase(),
11 | '0xe8b251822d003a2b2466ee0e38391c2db2048739'.toLowerCase(),
12 | '0x233d91a0713155003fc4dce0afa871b508b3b715'.toLowerCase(),
13 | '0x07150e919b4de5fd6a63de1f9384828396f25fdc'.toLowerCase(),
14 | '0x05462671c05adc39a6521fa60d5e9443e9e9d2b9'.toLowerCase(),
15 | '0xecbf566944250dde88322581024e611419715f7a'.toLowerCase(),
16 | '0x9248c485b0b80f76da451f167a8db30f33c70907'.toLowerCase(),
17 | '0x3936ad01cf109a36489d93cabda11cf062fd3d48'.toLowerCase(),
18 | '0x2f6081e3552b1c86ce4479b80062a1dda8ef23e3'.toLowerCase(),
19 | '0xe17f017475a709de58e976081eb916081ff4c9d5'.toLowerCase(),
20 | '0x87f5f9ebe40786d49d35e1b5997b07ccaa8adbff'.toLowerCase(),
21 | '0x98ad9b32dd10f8d8486927d846d4df8baf39abe2'.toLowerCase(),
22 | '0x7777770f8a6632ff043c8833310e245eba9209e6'.toLowerCase(),
23 | '0x3fa807b6f8d4c407e6e605368f4372d14658b38c'.toLowerCase(),
24 | '0x10bae51262490b4f4af41e12ed52a0e744c1137a'.toLowerCase(),
25 | '0xac6fe9aa6b996d15f23e2e9a384fe64607bba7d5'.toLowerCase(),
26 | '0x15e4132dcd932e8990e794d1300011a472819cbd'.toLowerCase(),
27 | '0x5166d4ce79b9bf7df477da110c560ce3045aa889'.toLowerCase(),
28 | '0xf911a7ec46a2c6fa49193212fe4a2a9b95851c27'.toLowerCase(),
29 | ];
30 |
31 | const BSC_REBASING_TOKENS = [
32 | '0x233d91a0713155003fc4dce0afa871b508b3b715'.toLowerCase(),
33 | ];
34 |
35 | export const isRebasingToken = token => {
36 | if (!token) return false;
37 | const { chainId, address } = token;
38 | switch (chainId) {
39 | case 56:
40 | return BSC_REBASING_TOKENS.includes(address.toLowerCase());
41 | case 1:
42 | return ETH_REBASING_TOKENS.includes(address.toLowerCase());
43 | default:
44 | return false;
45 | }
46 | };
47 |
48 | const ExceptionsLink = ({ msg }) => (
49 |
54 | {msg}
55 |
56 | );
57 |
58 | export const RebasingTokenWarning = ({ token, noShadow = false }) =>
59 | isRebasingToken(token) ? (
60 |
61 |
66 |
67 |
68 | {token.symbol} is a rebasing token and cannot be bridged.{' '}
69 | .
70 |
71 |
72 |
73 | ) : null;
74 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/SafeMoonTokenWarning.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Flex, Text } from '@chakra-ui/react';
2 | import React from 'react';
3 |
4 | const BSC_SAFEMOON_TOKENS = [
5 | '0x8076c74c5e3f5852037f31ff0093eeb8c8add8d3'.toLowerCase(),
6 | ];
7 |
8 | export const isSafeMoonToken = token => {
9 | if (!token) return false;
10 | const { chainId, address } = token;
11 | switch (chainId) {
12 | case 56:
13 | return BSC_SAFEMOON_TOKENS.includes(address.toLowerCase());
14 | default:
15 | return false;
16 | }
17 | };
18 |
19 | export const SafeMoonTokenWarning = ({ token, noShadow = false }) =>
20 | isSafeMoonToken(token) ? (
21 |
22 |
27 |
28 | SafeMoon tokens cannot be bridged.
29 |
30 |
31 | ) : null;
32 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/StakeTokenWarning.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertIcon, Flex, Link, Text } from '@chakra-ui/react';
2 | import React from 'react';
3 |
4 | const LearnMoreLink = () => (
5 |
10 | learn more
11 |
12 | );
13 |
14 | const XDAI_STAKE_TOKEN =
15 | '0xb7D311E2Eb55F2f68a9440da38e7989210b9A05e'.toLowerCase();
16 |
17 | export const isDisabledStakeToken = token => {
18 | if (!token) return false;
19 | const { chainId, address } = token;
20 | switch (chainId) {
21 | case 100:
22 | return address.toLowerCase() === XDAI_STAKE_TOKEN;
23 | default:
24 | return false;
25 | }
26 | };
27 |
28 | export const StakeTokenWarning = ({ token, noShadow = false }) =>
29 | isDisabledStakeToken(token) ? (
30 |
31 |
36 |
37 |
38 | Bridging of STAKE tokens is disabled, please swap your STAKE tokens
39 | for GNO tokens (
40 | ).
41 |
42 |
43 |
44 | ) : null;
45 |
--------------------------------------------------------------------------------
/packages/dapp/src/components/warnings/TokenWarnings.jsx:
--------------------------------------------------------------------------------
1 | import { BSCETHTokenWarnings } from 'components/warnings/BSCETHTokenWarnings';
2 | import { BSCGCTokenWarnings } from 'components/warnings/BSCGCTokenWarnings';
3 | import { DaiWarning } from 'components/warnings/DaiWarning';
4 | import { GCOriginOnBSCTokenWarning } from 'components/warnings/GCOriginOnBSCTokenWarning';
5 | import { RebasingTokenWarning } from 'components/warnings/RebasingTokenWarning';
6 | import { SafeMoonTokenWarning } from 'components/warnings/SafeMoonTokenWarning';
7 | import { StakeTokenWarning } from 'components/warnings/StakeTokenWarning';
8 | import React from 'react';
9 |
10 | export const TokenWarnings = ({ token, noShadow = false }) =>
11 | token ? (
12 | <>
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | >
21 | ) : null;
22 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useAmbVersion.js:
--------------------------------------------------------------------------------
1 | import { fetchAmbVersion } from 'lib/amb';
2 | import { getNetworkLabel, logError } from 'lib/helpers';
3 | import { getEthersProvider } from 'lib/providers';
4 | import { useEffect, useState } from 'react';
5 |
6 | export const useAmbVersion = (foreignChainId, foreignAmbAddress) => {
7 | const [foreignAmbVersion, setForeignAmbVersion] = useState();
8 | const [fetching, setFetching] = useState(false);
9 |
10 | useEffect(() => {
11 | const label = getNetworkLabel(foreignChainId).toUpperCase();
12 | const key = `${label}-AMB-VERSION`;
13 | const fetchVersion = async () => {
14 | const provider = await getEthersProvider(foreignChainId);
15 | await fetchAmbVersion(foreignAmbAddress, provider)
16 | .then(versionString => {
17 | setForeignAmbVersion(versionString);
18 | sessionStorage.setItem(key, versionString);
19 | })
20 | .catch(versionError => logError({ versionError }));
21 | setFetching(false);
22 | };
23 | const version = sessionStorage.getItem(key);
24 | if (!version && !fetching) {
25 | setFetching(true);
26 | fetchVersion();
27 | } else {
28 | setForeignAmbVersion(version);
29 | }
30 | }, [foreignAmbAddress, foreignChainId, fetching]);
31 |
32 | return foreignAmbVersion;
33 | };
34 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useApproval.js:
--------------------------------------------------------------------------------
1 | import { useWeb3Context } from 'contexts/Web3Context';
2 | import { BigNumber } from 'ethers';
3 | import { LARGEST_UINT256, LOCAL_STORAGE_KEYS } from 'lib/constants';
4 | import { logDebug, logError } from 'lib/helpers';
5 | import { approveToken, fetchAllowance } from 'lib/token';
6 | import { useCallback, useEffect, useState } from 'react';
7 |
8 | const { INFINITE_UNLOCK } = LOCAL_STORAGE_KEYS;
9 |
10 | export const useApproval = (fromToken, fromAmount, txHash) => {
11 | const { account, ethersProvider, providerChainId } = useWeb3Context();
12 | const [allowance, setAllowance] = useState(BigNumber.from(0));
13 | const [allowed, setAllowed] = useState(true);
14 |
15 | useEffect(() => {
16 | if (fromToken && providerChainId === fromToken.chainId) {
17 | fetchAllowance(fromToken, account, ethersProvider).then(setAllowance);
18 | } else {
19 | setAllowance(BigNumber.from(0));
20 | }
21 | }, [ethersProvider, account, fromToken, providerChainId, txHash]);
22 |
23 | useEffect(() => {
24 | setAllowed(
25 | (fromToken && ['NATIVE', 'erc677'].includes(fromToken.mode)) ||
26 | allowance.gte(fromAmount),
27 | );
28 | }, [fromAmount, allowance, fromToken]);
29 |
30 | const [unlockLoading, setUnlockLoading] = useState(false);
31 | const [approvalTxHash, setApprovalTxHash] = useState();
32 |
33 | const approve = useCallback(async () => {
34 | setUnlockLoading(true);
35 | const approvalAmount =
36 | window.localStorage.getItem(INFINITE_UNLOCK) === 'true'
37 | ? LARGEST_UINT256
38 | : fromAmount;
39 | try {
40 | const tx = await approveToken(ethersProvider, fromToken, approvalAmount);
41 | setApprovalTxHash(tx.hash);
42 | await tx.wait();
43 | setAllowance(approvalAmount);
44 | } catch (approveError) {
45 | if (approveError?.code === 'TRANSACTION_REPLACED') {
46 | if (approveError.cancelled) {
47 | throw new Error('transaction was replaced');
48 | } else {
49 | logDebug('TRANSACTION_REPLACED');
50 | setApprovalTxHash(approveError.replacement.hash);
51 | try {
52 | await approveError.replacement.wait();
53 | setAllowance(approvalAmount);
54 | } catch (secondApprovalError) {
55 | logError({
56 | secondApprovalError,
57 | fromToken,
58 | approvalAmount: approvalAmount.toString(),
59 | account,
60 | });
61 | throw secondApprovalError;
62 | }
63 | }
64 | } else {
65 | logError({
66 | approveError,
67 | fromToken,
68 | approvalAmount: approvalAmount.toString(),
69 | account,
70 | });
71 | throw approveError;
72 | }
73 | } finally {
74 | setApprovalTxHash();
75 | setUnlockLoading(false);
76 | }
77 | }, [fromAmount, fromToken, ethersProvider, account]);
78 |
79 | return { allowed, unlockLoading, approvalTxHash, approve };
80 | };
81 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useBridgeDirection.js:
--------------------------------------------------------------------------------
1 | import { useSettings } from 'contexts/SettingsContext';
2 | import { useAmbVersion } from 'hooks/useAmbVersion';
3 | import { useTotalConfirms } from 'hooks/useTotalConfirms';
4 | import { useValidatorsContract } from 'hooks/useValidatorsContract';
5 | import { networks } from 'lib/networks';
6 | import { useCallback, useMemo } from 'react';
7 |
8 | export const useBridgeDirection = () => {
9 | const { bridgeDirection } = useSettings();
10 | const bridgeConfig = useMemo(
11 | () => networks[bridgeDirection] || Object.values(networks)[0],
12 | [bridgeDirection],
13 | );
14 |
15 | const {
16 | homeChainId,
17 | foreignChainId,
18 | ambLiveMonitorPrefix,
19 | homeGraphName,
20 | foreignGraphName,
21 | homeAmbAddress,
22 | foreignAmbAddress,
23 | } = bridgeConfig;
24 |
25 | const foreignAmbVersion = useAmbVersion(foreignChainId, foreignAmbAddress);
26 |
27 | const { requiredSignatures, validatorList } = useValidatorsContract(
28 | foreignChainId,
29 | foreignAmbAddress,
30 | );
31 |
32 | const { homeTotalConfirms, foreignTotalConfirms } = useTotalConfirms(
33 | homeChainId,
34 | foreignChainId,
35 | homeAmbAddress,
36 | foreignAmbAddress,
37 | );
38 |
39 | const getBridgeChainId = useCallback(
40 | chainId => (chainId === homeChainId ? foreignChainId : homeChainId),
41 | [homeChainId, foreignChainId],
42 | );
43 |
44 | const getMonitorUrl = useCallback(
45 | (chainId, hash) => `${ambLiveMonitorPrefix}/${chainId}/${hash}`,
46 | [ambLiveMonitorPrefix],
47 | );
48 |
49 | const getGraphEndpoint = useCallback(
50 | chainId => {
51 | const subgraphName =
52 | homeChainId === chainId ? homeGraphName : foreignGraphName;
53 | return `https://api.thegraph.com/subgraphs/name/${subgraphName}`;
54 | },
55 | [foreignGraphName, homeChainId, homeGraphName],
56 | );
57 |
58 | const getAMBAddress = useCallback(
59 | chainId => (chainId === homeChainId ? homeAmbAddress : foreignAmbAddress),
60 | [homeChainId, homeAmbAddress, foreignAmbAddress],
61 | );
62 |
63 | const getTotalConfirms = useCallback(
64 | chainId =>
65 | chainId === homeChainId ? homeTotalConfirms : foreignTotalConfirms,
66 | [homeChainId, homeTotalConfirms, foreignTotalConfirms],
67 | );
68 |
69 | return {
70 | bridgeDirection,
71 | getBridgeChainId,
72 | getMonitorUrl,
73 | getGraphEndpoint,
74 | getAMBAddress,
75 | foreignAmbVersion,
76 | homeTotalConfirms,
77 | foreignTotalConfirms,
78 | getTotalConfirms,
79 | requiredSignatures,
80 | validatorList,
81 | ...bridgeConfig,
82 | };
83 | };
84 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useClaimableTransfers.js:
--------------------------------------------------------------------------------
1 | import { useBridgeContext } from 'contexts/BridgeContext';
2 | import { useWeb3Context } from 'contexts/Web3Context';
3 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
4 | import { useGraphHealth } from 'hooks/useGraphHealth';
5 | import {
6 | combineRequestsWithExecutions,
7 | getExecutions,
8 | getRequests,
9 | } from 'lib/history';
10 | import { useEffect, useState } from 'react';
11 |
12 | export const useClaimableTransfers = () => {
13 | const { homeChainId, foreignChainId, getGraphEndpoint } =
14 | useBridgeDirection();
15 | const { account } = useWeb3Context();
16 | const { txHash } = useBridgeContext();
17 | const [transfers, setTransfers] = useState();
18 | const [loading, setLoading] = useState(false);
19 |
20 | const { homeHealthy, foreignHealthy } = useGraphHealth();
21 | const subgraphHealthy = homeHealthy && foreignHealthy;
22 |
23 | useEffect(() => {
24 | if (!account) return () => undefined;
25 | let isSubscribed = true;
26 | async function update() {
27 | setLoading(true);
28 | setTransfers();
29 | const { requests } = await getRequests(
30 | account,
31 | getGraphEndpoint(homeChainId),
32 | );
33 | const { executions } = await getExecutions(
34 | getGraphEndpoint(foreignChainId),
35 | requests,
36 | );
37 | const homeTransfers = combineRequestsWithExecutions(
38 | requests,
39 | executions,
40 | homeChainId,
41 | foreignChainId,
42 | )
43 | .sort((a, b) => b.timestamp - a.timestamp)
44 | .filter(t => !t.receivingTx);
45 | if (isSubscribed) {
46 | setTransfers(homeTransfers);
47 | setLoading(false);
48 | }
49 | }
50 | update();
51 | return () => {
52 | isSubscribed = false;
53 | };
54 | }, [account, txHash, homeChainId, foreignChainId, getGraphEndpoint]);
55 |
56 | return { transfers: subgraphHealthy ? transfers : undefined, loading };
57 | };
58 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useCoinzillaText.js:
--------------------------------------------------------------------------------
1 | import { COINZILLA_API_KEY } from 'lib/constants';
2 | import { logError } from 'lib/helpers';
3 | import { useEffect, useState } from 'react';
4 |
5 | const COINZILLA_TEXT_API_URL = `https://request-global.czilladx.com/serve/native.php`;
6 |
7 | export const useCoinzillaText = () => {
8 | const [isFetching, setIsFetching] = useState(true);
9 | const [adFetchError, setAdFetchError] = useState(null);
10 | const [adData, setAdData] = useState(null);
11 |
12 | useEffect(() => {
13 | (async function fetchAdvertisement() {
14 | try {
15 | if (COINZILLA_API_KEY) {
16 | const res = await fetch(
17 | `${COINZILLA_TEXT_API_URL}?z=${COINZILLA_API_KEY}`,
18 | );
19 | if (res.ok) {
20 | const data = await res.json();
21 | setAdData(data);
22 | }
23 | }
24 | } catch (err) {
25 | logError(err);
26 | setAdFetchError(err);
27 | } finally {
28 | setIsFetching(false);
29 | }
30 | })();
31 | }, []);
32 |
33 | return {
34 | adData,
35 | isFetching,
36 | adFetchError,
37 | };
38 | };
39 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useCopyToClipboard.js:
--------------------------------------------------------------------------------
1 | import copyToClipboard from 'copy-to-clipboard';
2 | import { useCallback, useEffect, useState } from 'react';
3 |
4 | const DEFAULT_DELAY = 3000;
5 |
6 | export const useCopyToClipboard = (delay = DEFAULT_DELAY) => {
7 | const [copied, setCopied] = useState(false);
8 |
9 | useEffect(() => {
10 | if (!copied) return () => undefined;
11 |
12 | const id = setTimeout(() => {
13 | setCopied(false);
14 | }, delay);
15 |
16 | return () => {
17 | clearTimeout(id);
18 | };
19 | }, [delay, copied]);
20 |
21 | const handleCopy = useCallback(text => {
22 | setCopied(true);
23 | copyToClipboard(text);
24 | }, []);
25 |
26 | return [copied, handleCopy];
27 | };
28 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useENS.js:
--------------------------------------------------------------------------------
1 | import { ethers } from 'ethers';
2 | import { logError } from 'lib/helpers';
3 | import { getEthersProvider } from 'lib/providers';
4 | import { useEffect, useState } from 'react';
5 |
6 | export function useENS(address) {
7 | const [ensName, setENSName] = useState('');
8 |
9 | useEffect(() => {
10 | async function resolveENS() {
11 | try {
12 | if (ethers.utils.isAddress(address)) {
13 | const provider = await getEthersProvider(1);
14 | const name = await provider.lookupAddress(address);
15 | if (name) setENSName(name);
16 | }
17 | } catch (err) {
18 | logError({ ensError: err });
19 | }
20 | }
21 | resolveENS();
22 | }, [address]);
23 |
24 | return { ensName };
25 | }
26 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useETHPrice.js:
--------------------------------------------------------------------------------
1 | import { useUpdateInterval } from 'hooks/useUpdateInterval';
2 | import { useMemo } from 'react';
3 | import { getETHPrice } from 'stores/ethPrice';
4 |
5 | export const useETHPrice = () => {
6 | const [refreshCount] = useUpdateInterval();
7 |
8 | // eslint-disable-next-line react-hooks/exhaustive-deps
9 | return useMemo(() => getETHPrice(), [refreshCount]);
10 | };
11 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useGasPrice.js:
--------------------------------------------------------------------------------
1 | import { useUpdateInterval } from 'hooks/useUpdateInterval';
2 | import { useMemo } from 'react';
3 | import {
4 | getCurrentEthGasPrice,
5 | getHighestHistoricalEthGasPrice,
6 | getLowestHistoricalEthGasPrice,
7 | getMedianHistoricalEthGasPrice,
8 | } from 'stores/gasPrice';
9 |
10 | export const useGasPrice = () => {
11 | const [refreshCount] = useUpdateInterval();
12 |
13 | return useMemo(
14 | () => ({
15 | currentPrice: getCurrentEthGasPrice(),
16 | lowestPrice: getLowestHistoricalEthGasPrice(),
17 | medianPrice: getMedianHistoricalEthGasPrice(),
18 | highestPrice: getHighestHistoricalEthGasPrice(),
19 | }),
20 | // eslint-disable-next-line react-hooks/exhaustive-deps
21 | [refreshCount],
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useGraphHealth.js:
--------------------------------------------------------------------------------
1 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
2 | import { useMemo } from 'react';
3 | import { getGraphHealth } from 'stores/graphHealth';
4 | import { getRPCHealth } from 'stores/rpcHealth';
5 |
6 | import { useUpdateInterval } from './useUpdateInterval';
7 |
8 | const { REACT_APP_GRAPH_HEALTH_THRESHOLD_BLOCKS } = process.env;
9 |
10 | const DEFAULT_GRAPH_HEALTH_THRESHOLD_BLOCKS = 10;
11 |
12 | const THRESHOLD_BLOCKS =
13 | REACT_APP_GRAPH_HEALTH_THRESHOLD_BLOCKS ||
14 | DEFAULT_GRAPH_HEALTH_THRESHOLD_BLOCKS;
15 |
16 | export const useGraphHealth = () => {
17 | const { homeChainId, foreignChainId, homeGraphName, foreignGraphName } =
18 | useBridgeDirection();
19 |
20 | const [refreshCount] = useUpdateInterval();
21 |
22 | const [homeHealthy, foreignHealthy] = useMemo(() => {
23 | const {
24 | [homeChainId]: homeBlockNumber,
25 | [foreignChainId]: foreignBlockNumber,
26 | } = getRPCHealth();
27 | const { [homeGraphName]: homeHealth, [foreignGraphName]: foreignHealth } =
28 | getGraphHealth();
29 |
30 | const home =
31 | homeHealth && homeBlockNumber !== undefined
32 | ? homeHealth.isReachable &&
33 | !homeHealth.isFailed &&
34 | homeHealth.isSynced &&
35 | Math.abs(homeHealth.latestBlockNumber - homeBlockNumber) <
36 | THRESHOLD_BLOCKS &&
37 | Math.abs(homeHealth.chainHeadBlockNumber - homeBlockNumber) <
38 | THRESHOLD_BLOCKS
39 | : true;
40 |
41 | const foreign =
42 | foreignHealth && foreignBlockNumber !== undefined
43 | ? foreignHealth.isReachable &&
44 | !foreignHealth.isFailed &&
45 | foreignHealth.isSynced &&
46 | Math.abs(foreignHealth.latestBlockNumber - foreignBlockNumber) <
47 | THRESHOLD_BLOCKS &&
48 | Math.abs(foreignHealth.chainHeadBlockNumber - foreignBlockNumber) <
49 | THRESHOLD_BLOCKS
50 | : true;
51 |
52 | return [home, foreign];
53 | // for refreshCount
54 | // eslint-disable-next-line react-hooks/exhaustive-deps
55 | }, [
56 | refreshCount,
57 | homeChainId,
58 | foreignChainId,
59 | homeGraphName,
60 | foreignGraphName,
61 | ]);
62 |
63 | return { homeHealthy, foreignHealthy };
64 | };
65 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useLocalState.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useMemo, useState } from 'react';
2 |
3 | export const useLocalState = (
4 | initialValue,
5 | key,
6 | { valueType = 'string', isStoredImmediately = false } = {},
7 | ) => {
8 | const storageValue = useMemo(() => window.localStorage.getItem(key), [key]);
9 | const castedValue = useMemo(() => {
10 | if (valueType === 'number') {
11 | return parseInt(storageValue, 10);
12 | }
13 | if (valueType === 'boolean') {
14 | return storageValue === 'true';
15 | }
16 | if (valueType === 'object') {
17 | return JSON.parse(storageValue);
18 | }
19 | return storageValue;
20 | }, [storageValue, valueType]);
21 |
22 | const [value, setValue] = useState(castedValue || initialValue);
23 |
24 | const updateValue = useCallback(
25 | (val, shouldBeStored = false) => {
26 | const result = typeof val === 'function' ? val(value) : val;
27 | if (JSON.stringify(result) !== JSON.stringify(value)) {
28 | setValue(result);
29 | }
30 | if ((!!isStoredImmediately || !!shouldBeStored) && !!key) {
31 | window.localStorage.setItem(key, result);
32 | }
33 | },
34 | [key, value, isStoredImmediately],
35 | );
36 |
37 | useEffect(() => {
38 | updateValue(value);
39 | }, [key, value, updateValue]);
40 |
41 | useEffect(() => {
42 | if (!!key && !storageValue) {
43 | localStorage.setItem(key, initialValue);
44 | }
45 | }, [key, initialValue, storageValue]);
46 |
47 | return [value, updateValue, castedValue];
48 | };
49 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useNeedsClaiming.js:
--------------------------------------------------------------------------------
1 | import { useBridgeContext } from 'contexts/BridgeContext';
2 | import { useWeb3Context } from 'contexts/Web3Context';
3 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
4 | import { useMemo } from 'react';
5 |
6 | export const useNeedsClaiming = () => {
7 | const { providerChainId } = useWeb3Context();
8 | const { fromToken } = useBridgeContext();
9 | const { homeChainId, claimDisabled, tokensClaimDisabled } =
10 | useBridgeDirection();
11 |
12 | const isHome = providerChainId === homeChainId;
13 |
14 | return useMemo(
15 | () =>
16 | isHome &&
17 | !claimDisabled &&
18 | !(tokensClaimDisabled ?? []).includes(fromToken?.address.toLowerCase()),
19 | [isHome, claimDisabled, tokensClaimDisabled, fromToken],
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useRPCHealth.js:
--------------------------------------------------------------------------------
1 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
2 | import { useUpdateInterval } from 'hooks/useUpdateInterval';
3 | import { useMemo } from 'react';
4 | import { getRPCHealth } from 'stores/rpcHealth';
5 |
6 | export const useRPCHealth = () => {
7 | const { homeChainId, foreignChainId } = useBridgeDirection();
8 |
9 | const [refreshCount] = useUpdateInterval();
10 |
11 | const { [homeChainId]: homeHealthy, [foreignChainId]: foreignHealthy } =
12 | // eslint-disable-next-line react-hooks/exhaustive-deps
13 | useMemo(() => getRPCHealth(), [refreshCount]);
14 |
15 | return {
16 | homeHealthy: homeHealthy === undefined ? true : homeHealthy,
17 | foreignHealthy: foreignHealthy === undefined ? true : foreignHealthy,
18 | };
19 | };
20 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useRefresh.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useState } from 'react';
2 |
3 | export const useRefresh = () => {
4 | const [refreshCount, setRefreshCount] = useState(0);
5 | const refresh = useCallback(() => setRefreshCount(c => c + 1), []);
6 | return [refreshCount, refresh];
7 | };
8 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useRenderChain.jsx:
--------------------------------------------------------------------------------
1 | import { Badge, Image, Tooltip } from '@chakra-ui/react';
2 | import MetamaskFox from 'assets/metamask-fox.svg';
3 | import { useWeb3Context } from 'contexts/Web3Context';
4 | import { useSwitchChain } from 'hooks/useSwitchChain';
5 | import { getNetworkName } from 'lib/helpers';
6 | import React, { useCallback } from 'react';
7 |
8 | export const useRenderChain = () => {
9 | const { isMetamask } = useWeb3Context();
10 |
11 | const switchChain = useSwitchChain();
12 |
13 | const renderChain = useCallback(
14 | chainId => {
15 | const networkName = getNetworkName(chainId);
16 | const isDefaultChain = [1, 3, 4, 5, 42].includes(chainId);
17 | const isMobileBrowser = navigator?.userAgent?.includes('Mobile') || false;
18 | const buttonWillWork =
19 | isMetamask && (isMobileBrowser ? !isDefaultChain : true);
20 |
21 | return buttonWillWork ? (
22 |
23 | switchChain(chainId)}
34 | >
35 |
36 | {networkName}
37 |
38 |
39 | ) : (
40 |
50 | {networkName}
51 |
52 | );
53 | },
54 | [switchChain, isMetamask],
55 | );
56 |
57 | return renderChain;
58 | };
59 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useSwitchChain.js:
--------------------------------------------------------------------------------
1 | import { useToast } from '@chakra-ui/react';
2 | import { handleWalletError, logError } from 'lib/helpers';
3 | import { addChainToMetaMask } from 'lib/metamask';
4 | import { useCallback } from 'react';
5 |
6 | export const useSwitchChain = () => {
7 | const toast = useToast();
8 |
9 | const showError = useCallback(
10 | msg => {
11 | if (msg) {
12 | toast({
13 | title: 'Error',
14 | description: msg,
15 | status: 'error',
16 | isClosable: 'true',
17 | });
18 | }
19 | },
20 | [toast],
21 | );
22 |
23 | return useCallback(
24 | async chainId => {
25 | const result = await addChainToMetaMask(chainId).catch(metamaskError => {
26 | logError({ metamaskError });
27 | handleWalletError(metamaskError, showError);
28 | });
29 | return result || false;
30 | },
31 | [showError],
32 | );
33 | };
34 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useTokenDisabled.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | isBSCPeggedToken,
3 | isGCStableToken,
4 | } from 'components/warnings/BSCGCTokenWarnings';
5 | import { isERC20DaiAddress } from 'components/warnings/DaiWarning';
6 | import { isRebasingToken } from 'components/warnings/RebasingTokenWarning';
7 | import { isSafeMoonToken } from 'components/warnings/SafeMoonTokenWarning';
8 | import { isDisabledStakeToken } from 'components/warnings/StakeTokenWarning';
9 | import { Contract } from 'ethers';
10 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
11 | import {
12 | ADDRESS_ZERO,
13 | BSC_XDAI_BRIDGE,
14 | ETH_BSC_BRIDGE,
15 | ETH_XDAI_BRIDGE,
16 | } from 'lib/constants';
17 | import { logError } from 'lib/helpers';
18 | import { networks } from 'lib/networks';
19 | import { getEthersProvider } from 'lib/providers';
20 | import { useCallback, useEffect, useState } from 'react';
21 |
22 | const GC_BSC_OMNIBRIDGE = networks[BSC_XDAI_BRIDGE].foreignMediatorAddress;
23 |
24 | export const useTokenGCOriginOnBSC = token => {
25 | const { bridgeDirection } = useBridgeDirection();
26 | const [fetching, setFetching] = useState(true);
27 | const [isToken, setIsToken] = useState(false);
28 | const load = useCallback(async () => {
29 | setFetching(true);
30 | try {
31 | if (token && token.chainId === 56 && bridgeDirection === ETH_BSC_BRIDGE) {
32 | const provider = await getEthersProvider(56);
33 | const abi = [
34 | 'function nativeTokenAddress(address) view returns (address)',
35 | ];
36 | const contract = new Contract(GC_BSC_OMNIBRIDGE, abi, provider);
37 |
38 | const address = await contract.nativeTokenAddress(token.address);
39 | setIsToken(address !== ADDRESS_ZERO);
40 | } else {
41 | setIsToken(false);
42 | }
43 | } catch (error) {
44 | logError({ message: 'Error fetching nativeTokenAddress', error });
45 | setIsToken(false);
46 | } finally {
47 | setFetching(false);
48 | }
49 | }, [token, bridgeDirection]);
50 |
51 | useEffect(() => load(), [load]);
52 | return { fetching, isToken };
53 | };
54 |
55 | export const useTokenDisabled = token => {
56 | const { bridgeDirection } = useBridgeDirection();
57 | const { isToken: isTokenGCOriginOnBSC, fetching } =
58 | useTokenGCOriginOnBSC(token);
59 |
60 | if (!token || fetching) return false;
61 | const isTokenRebasing = isRebasingToken(token);
62 | const isTokenSafeMoon = isSafeMoonToken(token);
63 | const isTokenDisabledStake = isDisabledStakeToken(token);
64 | const isTokenBSCPegged =
65 | isBSCPeggedToken(token) && bridgeDirection === BSC_XDAI_BRIDGE;
66 | const isTokenGCStableToBSC =
67 | isGCStableToken(token) && bridgeDirection === BSC_XDAI_BRIDGE;
68 | const isTokenDAI =
69 | isERC20DaiAddress(token) && bridgeDirection === ETH_XDAI_BRIDGE;
70 | return (
71 | isTokenRebasing ||
72 | isTokenSafeMoon ||
73 | isTokenDisabledStake ||
74 | isTokenGCStableToBSC ||
75 | isTokenGCOriginOnBSC ||
76 | isTokenDAI ||
77 | isTokenBSCPegged
78 | );
79 | };
80 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useTokenLimits.js:
--------------------------------------------------------------------------------
1 | import { useBridgeContext } from 'contexts/BridgeContext';
2 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
3 | import { fetchTokenLimits } from 'lib/bridge';
4 |
5 | const { useState, useCallback, useEffect } = require('react');
6 |
7 | export const useTokenLimits = () => {
8 | const { fromToken, toToken, currentDay } = useBridgeContext();
9 | const { bridgeDirection, homeChainId, foreignChainId } = useBridgeDirection();
10 | const [tokenLimits, setTokenLimits] = useState();
11 | const [fetching, setFetching] = useState(false);
12 |
13 | const updateTokenLimits = useCallback(async () => {
14 | if (
15 | fromToken &&
16 | toToken &&
17 | fromToken.chainId &&
18 | toToken.chainId &&
19 | (fromToken.symbol.includes(toToken.symbol) ||
20 | toToken.symbol.includes(fromToken.symbol)) &&
21 | [homeChainId, foreignChainId].includes(fromToken.chainId) &&
22 | [homeChainId, foreignChainId].includes(toToken.chainId) &&
23 | bridgeDirection &&
24 | currentDay
25 | ) {
26 | setFetching(true);
27 | const limits = await fetchTokenLimits(
28 | bridgeDirection,
29 | fromToken,
30 | toToken,
31 | currentDay,
32 | );
33 | setTokenLimits(limits);
34 | setFetching(false);
35 | }
36 | }, [
37 | fromToken,
38 | toToken,
39 | bridgeDirection,
40 | homeChainId,
41 | foreignChainId,
42 | currentDay,
43 | ]);
44 |
45 | useEffect(() => updateTokenLimits(), [updateTokenLimits]);
46 |
47 | return { tokenLimits, fetching, refresh: updateTokenLimits };
48 | };
49 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useTotalConfirms.js:
--------------------------------------------------------------------------------
1 | import { fetchConfirmations } from 'lib/amb';
2 | import { getNetworkLabel, logError } from 'lib/helpers';
3 | import { getEthersProvider } from 'lib/providers';
4 | import { useEffect, useState } from 'react';
5 |
6 | export const useTotalConfirms = (
7 | homeChainId,
8 | foreignChainId,
9 | homeAmbAddress,
10 | foreignAmbAddress,
11 | ) => {
12 | const [homeTotalConfirms, setHomeTotalConfirms] = useState(8);
13 | const [foreignTotalConfirms, setForeignTotalConfirms] = useState(8);
14 |
15 | useEffect(() => {
16 | const homeLabel = getNetworkLabel(homeChainId).toUpperCase();
17 | const homeKey = `${homeLabel}-${homeAmbAddress.toUpperCase()}-TOTAL-CONFIRMATIONS`;
18 |
19 | const foreignLabel = getNetworkLabel(foreignChainId).toUpperCase();
20 | const foreignKey = `${foreignLabel}-${foreignAmbAddress.toUpperCase()}-TOTAL-CONFIRMATIONS`;
21 |
22 | const fetchConfirms = async () => {
23 | try {
24 | const [homeProvider, foreignProvider] = await Promise.all([
25 | getEthersProvider(homeChainId),
26 | getEthersProvider(foreignChainId),
27 | ]);
28 | const [home, foreign] = await Promise.all([
29 | fetchConfirmations(homeAmbAddress, homeProvider),
30 | fetchConfirmations(foreignAmbAddress, foreignProvider),
31 | ]);
32 | setHomeTotalConfirms(home);
33 | setForeignTotalConfirms(foreign);
34 | sessionStorage.setItem(homeKey, home);
35 | sessionStorage.setItem(foreignKey, foreign);
36 | } catch (confirmsError) {
37 | logError({ confirmsError });
38 | }
39 | };
40 |
41 | const homeConfirms = sessionStorage.getItem(homeKey);
42 | const foreignConfirms = sessionStorage.getItem(foreignKey);
43 | if (homeConfirms && foreignConfirms) {
44 | setHomeTotalConfirms(Number.parseInt(homeConfirms, 10));
45 | setForeignTotalConfirms(Number.parseInt(foreignConfirms, 10));
46 | } else {
47 | fetchConfirms();
48 | }
49 | }, [homeChainId, foreignChainId, homeAmbAddress, foreignAmbAddress]);
50 |
51 | return { homeTotalConfirms, foreignTotalConfirms };
52 | };
53 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useUpdateInterval.js:
--------------------------------------------------------------------------------
1 | import { useRefresh } from 'hooks/useRefresh';
2 | import { POLLING_INTERVAL } from 'lib/constants';
3 | import { useEffect } from 'react';
4 |
5 | export const useUpdateInterval = () => {
6 | const [refreshCount, refresh] = useRefresh();
7 |
8 | useEffect(() => {
9 | const id = setInterval(refresh, POLLING_INTERVAL);
10 | return () => clearInterval(id);
11 | }, [refresh]);
12 |
13 | return [refreshCount, refresh];
14 | };
15 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useUserHistory.js:
--------------------------------------------------------------------------------
1 | import { useWeb3Context } from 'contexts/Web3Context';
2 | import { useBridgeDirection } from 'hooks/useBridgeDirection';
3 | import {
4 | combineRequestsWithExecutions,
5 | getExecutions,
6 | getRequests,
7 | } from 'lib/history';
8 | import { useEffect, useState } from 'react';
9 |
10 | export const useUserHistory = () => {
11 | const { homeChainId, foreignChainId, getGraphEndpoint } =
12 | useBridgeDirection();
13 | const { account } = useWeb3Context();
14 | const [transfers, setTransfers] = useState([]);
15 | const [loading, setLoading] = useState(true);
16 |
17 | useEffect(() => {
18 | if (!account) {
19 | setLoading(false);
20 | return () => undefined;
21 | }
22 | let isSubscribed = true;
23 | async function update() {
24 | setTransfers();
25 | setLoading(true);
26 | const [{ requests: homeRequests }, { requests: foreignRequests }] =
27 | await Promise.all([
28 | getRequests(account, getGraphEndpoint(homeChainId)),
29 | getRequests(account, getGraphEndpoint(foreignChainId)),
30 | ]);
31 | const [
32 | { executions: homeExecutions },
33 | { executions: foreignExecutions },
34 | ] = await Promise.all([
35 | getExecutions(getGraphEndpoint(homeChainId), foreignRequests),
36 | getExecutions(getGraphEndpoint(foreignChainId), homeRequests),
37 | ]);
38 | const homeTransfers = combineRequestsWithExecutions(
39 | homeRequests,
40 | foreignExecutions,
41 | homeChainId,
42 | foreignChainId,
43 | );
44 | const foreignTransfers = combineRequestsWithExecutions(
45 | foreignRequests,
46 | homeExecutions,
47 | foreignChainId,
48 | homeChainId,
49 | );
50 | const allTransfers = [...homeTransfers, ...foreignTransfers].sort(
51 | (a, b) => b.timestamp - a.timestamp,
52 | );
53 | if (isSubscribed) {
54 | setTransfers(allTransfers);
55 | setLoading(false);
56 | }
57 | }
58 |
59 | update();
60 |
61 | return () => {
62 | isSubscribed = false;
63 | };
64 | }, [homeChainId, foreignChainId, account, getGraphEndpoint]);
65 |
66 | return { transfers, loading };
67 | };
68 |
--------------------------------------------------------------------------------
/packages/dapp/src/hooks/useValidatorsContract.js:
--------------------------------------------------------------------------------
1 | import { getNetworkLabel, logError } from 'lib/helpers';
2 | import { fetchRequiredSignatures, fetchValidatorList } from 'lib/message';
3 | import { getEthersProvider } from 'lib/providers';
4 | import { useEffect, useState } from 'react';
5 |
6 | export const useValidatorsContract = (foreignChainId, foreignAmbAddress) => {
7 | const [requiredSignatures, setRequiredSignatures] = useState(0);
8 | const [validatorList, setValidatorList] = useState([]);
9 |
10 | useEffect(() => {
11 | const label = getNetworkLabel(foreignChainId).toUpperCase();
12 | const key = `${label}-${foreignAmbAddress.toUpperCase()}-REQUIRED-SIGNATURES`;
13 | const fetchValue = async () => {
14 | try {
15 | const provider = await getEthersProvider(foreignChainId);
16 | const res = await fetchRequiredSignatures(foreignAmbAddress, provider);
17 | const signatures = Number.parseInt(res.toString(), 10);
18 | setRequiredSignatures(signatures);
19 | sessionStorage.setItem(key, signatures);
20 | } catch (versionError) {
21 | logError({ versionError });
22 | }
23 | };
24 | const storedValue = sessionStorage.getItem(key);
25 | if (storedValue) {
26 | setRequiredSignatures(Number.parseInt(storedValue, 10));
27 | } else {
28 | fetchValue();
29 | }
30 | }, [foreignAmbAddress, foreignChainId]);
31 |
32 | useEffect(() => {
33 | const label = getNetworkLabel(foreignChainId).toUpperCase();
34 | const key = `${label}-${foreignAmbAddress.toUpperCase()}-VALIDATOR-LIST`;
35 | const fetchValue = async () => {
36 | try {
37 | const provider = await getEthersProvider(foreignChainId);
38 | const res = await fetchValidatorList(foreignAmbAddress, provider);
39 | setValidatorList(res);
40 | sessionStorage.setItem(key, JSON.stringify(res));
41 | } catch (versionError) {
42 | logError({ versionError });
43 | }
44 | };
45 | const storedValue = sessionStorage.getItem(key);
46 | if (storedValue) {
47 | setValidatorList(JSON.parse(storedValue));
48 | } else {
49 | fetchValue();
50 | }
51 | }, [foreignAmbAddress, foreignChainId]);
52 |
53 | return { requiredSignatures, validatorList };
54 | };
55 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/DownArrowIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const DownArrowIcon = createIcon({
5 | displayName: 'DownArrowIcon',
6 | path: ,
7 | viewBox: '0 0 6 3',
8 | });
9 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/GithubIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const GithubIcon = createIcon({
5 | displayName: 'GithubIcon',
6 | path: (
7 |
11 | ),
12 | viewBox: '0 0 18 18',
13 | });
14 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/GnosisChainIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const GnosisChainIcon = createIcon({
5 | displayName: 'GnosisChainIcon',
6 | path: (
7 |
8 |
12 |
16 |
20 |
24 |
25 | ),
26 | viewBox: '0 0 600 600',
27 | });
28 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/HistoryIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const HistoryIcon = createIcon({
5 | displayName: 'HistoryIcon',
6 | path: (
7 | <>
8 |
12 |
18 | >
19 | ),
20 | viewBox: '0 0 18 18',
21 | });
22 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/LeftIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const LeftIcon = createIcon({
5 | displayName: 'LeftIcon',
6 | path: (
7 |
13 | ),
14 | viewBox: '0 0 23 24',
15 | });
16 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/LimitsIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const LimitsIcon = createIcon({
5 | displayName: 'LimitsIcon',
6 | path: (
7 |
13 | ),
14 | viewBox: '0 0 20 20',
15 | });
16 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/NetworkIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const NetworkIcon = createIcon({
5 | displayName: 'NetworkIcon',
6 | path: (
7 |
13 | ),
14 | viewBox: '0 0 18 18',
15 | });
16 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/OmniBridgeIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const OmniBridgeIcon = createIcon({
5 | displayName: 'OmniBridgeIcon',
6 | path: (
7 |
11 | ),
12 | viewBox: '0 0 31 21',
13 | });
14 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/PlusIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const PlusIcon = createIcon({
5 | displayName: 'PlusIcon',
6 | path: (
7 |
13 | ),
14 | viewBox: '0 0 14 14',
15 | });
16 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/RightIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const RightIcon = createIcon({
5 | displayName: 'RightIcon',
6 | path: (
7 |
13 | ),
14 | viewBox: '0 0 23 24',
15 | });
16 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/SettingsIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const SettingsIcon = createIcon({
5 | displayName: 'SettingsIcon',
6 | path: (
7 | <>
8 |
12 |
16 |
20 |
24 |
28 |
32 | >
33 | ),
34 | viewBox: '0 0 18 18',
35 | });
36 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/SwitchIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const SwitchIcon = createIcon({
5 | displayName: 'SwitchIcon',
6 | path: (
7 |
8 |
12 |
16 |
17 | ),
18 | viewBox: '0 0 64 64',
19 | });
20 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/TelegramIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const TelegramIcon = createIcon({
5 | displayName: 'TelegramIcon',
6 | path: (
7 |
11 | ),
12 | viewBox: '0 0 18 16',
13 | });
14 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/TwitterIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const TwitterIcon = createIcon({
5 | displayName: 'TwitterIcon',
6 | path: (
7 |
11 | ),
12 | viewBox: '0 0 18 16',
13 | });
14 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/WalletFilledIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const WalletFilledIcon = createIcon({
5 | displayName: 'WalletFilledIcon',
6 | path: (
7 |
13 | ),
14 | viewBox: '0 0 28 28',
15 | });
16 |
--------------------------------------------------------------------------------
/packages/dapp/src/icons/WalletIcon.jsx:
--------------------------------------------------------------------------------
1 | import { createIcon } from '@chakra-ui/icons';
2 | import * as React from 'react';
3 |
4 | export const WalletIcon = createIcon({
5 | displayName: 'WalletIcon',
6 | path: (
7 | <>
8 |
12 |
18 | >
19 | ),
20 | viewBox: '0 0 18 18',
21 | });
22 |
--------------------------------------------------------------------------------
/packages/dapp/src/index.jsx:
--------------------------------------------------------------------------------
1 | import * as Sentry from '@sentry/react';
2 | import { BrowserTracing } from '@sentry/tracing';
3 | import { SENTRY_DSN } from 'lib/constants';
4 | import React, { StrictMode } from 'react';
5 | import ReactDOM from 'react-dom';
6 |
7 | import { App } from './App';
8 |
9 | Sentry.init({
10 | dsn: SENTRY_DSN,
11 | tracesSampleRate: 1.0,
12 | debug: process.env.REACT_APP_DEBUG_LOGS === 'true',
13 | integrations: ints => [
14 | ...ints.filter(int => int.name !== 'Dedupe'),
15 | new BrowserTracing(),
16 | ],
17 | });
18 |
19 | ReactDOM.render(
20 |
21 |
22 | ,
23 | document.getElementById('root'),
24 | );
25 |
--------------------------------------------------------------------------------
/packages/dapp/src/lib/chainalysis.js:
--------------------------------------------------------------------------------
1 | import { Contract, utils } from 'ethers';
2 |
3 | import { logError } from './helpers';
4 | import { getEthersProvider } from './providers';
5 |
6 | const CHAINALYSIS_ORACLE_CONTRACT_ADDRESS =
7 | '0x40c57923924b5c5c5455c48d93317139addac8fb';
8 |
9 | export const isSanctionedByChainalysis = async address => {
10 | const provider = await getEthersProvider(1);
11 | const abi = new utils.Interface([
12 | 'function isSanctioned(address) view returns (bool)',
13 | ]);
14 | const oracle = new Contract(
15 | CHAINALYSIS_ORACLE_CONTRACT_ADDRESS,
16 | abi,
17 | provider,
18 | );
19 | let isSanctioned = false;
20 | try {
21 | isSanctioned = await oracle.isSanctioned(address);
22 | } catch (error) {
23 | logError('Chainalysis Oracle Error:', error);
24 | }
25 | return isSanctioned;
26 | };
27 |
--------------------------------------------------------------------------------
/packages/dapp/src/lib/graphHealth.js:
--------------------------------------------------------------------------------
1 | import { gql, request } from 'graphql-request';
2 | import { GRAPH_HEALTH_ENDPOINT } from 'lib/constants';
3 | import { logError } from 'lib/helpers';
4 |
5 | const healthQuery = gql`
6 | query getHealthStatus($subgraph: String!) {
7 | status: indexingStatusForCurrentVersion(subgraphName: $subgraph) {
8 | synced
9 | health
10 | fatalError {
11 | message
12 | block {
13 | number
14 | hash
15 | }
16 | handler
17 | }
18 | chains {
19 | chainHeadBlock {
20 | number
21 | }
22 | latestBlock {
23 | number
24 | }
25 | }
26 | }
27 | }
28 | `;
29 |
30 | const extractStatus = ({ fatalError, synced, chains }) => ({
31 | isReachable: true,
32 | isFailed: !!fatalError,
33 | isSynced: synced,
34 | latestBlockNumber: Number(chains[0].latestBlock.number),
35 | chainHeadBlockNumber: Number(chains[0].chainHeadBlock.number),
36 | });
37 |
38 | const failedStatus = {
39 | isReachable: false,
40 | isFailed: true,
41 | isSynced: false,
42 | latestBlockNumber: 0,
43 | chainHeadBlockNumber: 0,
44 | };
45 |
46 | export const getHealthStatus = async subgraph => {
47 | try {
48 | const data = await request(GRAPH_HEALTH_ENDPOINT, healthQuery, {
49 | subgraph,
50 | });
51 | return extractStatus(data.status);
52 | } catch (graphHealthError) {
53 | logError(`Error getting subgraph health for ${subgraph}`, graphHealthError);
54 | return failedStatus;
55 | }
56 | };
57 |
--------------------------------------------------------------------------------
/packages/dapp/src/lib/metamask.js:
--------------------------------------------------------------------------------
1 | import { utils } from 'ethers';
2 | import {
3 | getExplorerUrl,
4 | getNetworkCurrency,
5 | getNetworkName,
6 | getRPCUrl,
7 | logError,
8 | } from 'lib/helpers';
9 |
10 | export const addTokenToMetamask = async ({ address, symbol, decimals }) =>
11 | window.ethereum.request({
12 | method: 'wallet_watchAsset',
13 | params: {
14 | type: 'ERC20',
15 | options: {
16 | address,
17 | symbol,
18 | decimals,
19 | },
20 | },
21 | });
22 |
23 | const trySwitchChain = async chainId =>
24 | window.ethereum.request({
25 | method: 'wallet_switchEthereumChain',
26 | params: [
27 | {
28 | chainId: utils.hexValue(chainId),
29 | },
30 | ],
31 | });
32 |
33 | const tryAddChain = async (chainId, currency) =>
34 | window.ethereum.request({
35 | method: 'wallet_addEthereumChain',
36 | params: [
37 | {
38 | chainId: utils.hexValue(chainId),
39 | chainName: getNetworkName(chainId),
40 | nativeCurrency: currency,
41 | rpcUrls: [getRPCUrl(chainId)],
42 | blockExplorerUrls: [getExplorerUrl(chainId)],
43 | },
44 | ],
45 | });
46 |
47 | export const addChainToMetaMask = async chainId => {
48 | const { name, symbol } = getNetworkCurrency(chainId);
49 | const currency = { name, symbol, decimals: 18 };
50 |
51 | const add = ![1, 3, 4, 5, 42].includes(chainId);
52 | if (add) {
53 | try {
54 | await tryAddChain(chainId, currency);
55 | return true;
56 | } catch (addError) {
57 | logError({ addError });
58 | }
59 | return false;
60 | }
61 |
62 | try {
63 | await trySwitchChain(chainId);
64 | return true;
65 | } catch (switchError) {
66 | // This error code indicates that the chain has not been added to MetaMask.
67 | if (switchError.code === 4902) {
68 | try {
69 | await tryAddChain(chainId, currency);
70 | return true;
71 | } catch (addError) {
72 | logError({ addError });
73 | }
74 | } else {
75 | logError({ switchError });
76 | }
77 | }
78 | return false;
79 | };
80 |
--------------------------------------------------------------------------------
/packages/dapp/src/lib/providerHelpers.js:
--------------------------------------------------------------------------------
1 | import { ethers } from 'ethers';
2 | import memoize from 'fast-memoize';
3 | import { LOCAL_STORAGE_KEYS } from 'lib/constants';
4 | import { getNetworkLabel, getRPCUrl, logError } from 'lib/helpers';
5 |
6 | const {
7 | MAINNET_RPC_URL,
8 | KOVAN_RPC_URL,
9 | BSC_RPC_URL,
10 | SOKOL_RPC_URL,
11 | POA_RPC_URL,
12 | XDAI_RPC_URL,
13 | } = LOCAL_STORAGE_KEYS;
14 |
15 | const LOCAL_STORAGE_KEYS_MAP = {
16 | 1: MAINNET_RPC_URL,
17 | 42: KOVAN_RPC_URL,
18 | 56: BSC_RPC_URL,
19 | 77: SOKOL_RPC_URL,
20 | 99: POA_RPC_URL,
21 | 100: XDAI_RPC_URL,
22 | };
23 |
24 | const NETWORK_TIMEOUT = 1000;
25 |
26 | const memoized = memoize(
27 | url => new ethers.providers.StaticJsonRpcProvider(url),
28 | );
29 |
30 | const checkRPCHealth = async url => {
31 | if (!url) return null;
32 | const tempProvider = memoized(url);
33 | if (!tempProvider) return null;
34 | try {
35 | await Promise.race([
36 | // eslint-disable-next-line no-underscore-dangle
37 | tempProvider._networkPromise,
38 | setTimeout(
39 | () => Promise.reject(new Error('Network timeout')).catch(() => null),
40 | NETWORK_TIMEOUT,
41 | ),
42 | ]);
43 | return tempProvider;
44 | } catch (err) {
45 | logError({ providerSetError: err.message });
46 | return null;
47 | }
48 | };
49 |
50 | export const getValidEthersProvider = async chainId => {
51 | const label = getNetworkLabel(chainId).toUpperCase();
52 | const sessionStorageKey = `HEALTHY-RPC-URL-${label}`;
53 |
54 | const sessionRPCUrl = window.sessionStorage.getItem(sessionStorageKey);
55 | const localRPCUrl = window.localStorage.getItem(
56 | LOCAL_STORAGE_KEYS_MAP[chainId],
57 | );
58 |
59 | const rpcURLs = getRPCUrl(chainId, true);
60 |
61 | const provider =
62 | (await checkRPCHealth(localRPCUrl)) ??
63 | (await checkRPCHealth(sessionRPCUrl)) ??
64 | (await Promise.all(rpcURLs.map(checkRPCHealth))).filter(p => !!p)[0];
65 | if (provider?.connection?.url) {
66 | window.sessionStorage.setItem(sessionStorageKey, provider.connection.url);
67 | }
68 |
69 | return provider ?? null;
70 | };
71 |
--------------------------------------------------------------------------------
/packages/dapp/src/lib/providers.js:
--------------------------------------------------------------------------------
1 | import { setRPCHealth } from 'stores/rpcHealth';
2 |
3 | import { getValidEthersProvider } from './providerHelpers';
4 |
5 | export const getEthersProvider = async chainId => {
6 | const provider = await getValidEthersProvider(chainId);
7 | if (provider) {
8 | provider
9 | .getBlockNumber()
10 | .then(health => setRPCHealth(chainId, health))
11 | .catch();
12 | }
13 | return provider;
14 | };
15 |
16 | export const isEIP1193 = ethersProvider =>
17 | ethersProvider &&
18 | ethersProvider.connection &&
19 | ethersProvider.connection.url &&
20 | ethersProvider.connection.url.includes('eip-1193');
21 |
--------------------------------------------------------------------------------
/packages/dapp/src/lib/tokenList.js:
--------------------------------------------------------------------------------
1 | import schema from '@uniswap/token-lists/src/tokenlist.schema.json';
2 | import Ajv from 'ajv';
3 | import { gql, request } from 'graphql-request';
4 |
5 | import { getTokenListUrl, logError, uniqueTokens } from './helpers';
6 | import { renamexDaiTokensAsGnosis } from './token';
7 |
8 | const tokenListValidator = new Ajv({ allErrors: true }).compile(schema);
9 |
10 | const fetchDefaultTokens = async chainId => {
11 | try {
12 | const url = getTokenListUrl(chainId);
13 | if (url) {
14 | const response = await fetch(url);
15 | if (response.ok) {
16 | const json = await response.json();
17 | if (chainId === 56) {
18 | json.tokens = json.tokens.map(token => ({ ...token, chainId }));
19 | }
20 | if (tokenListValidator(json) || chainId === 56) {
21 | return json.tokens.filter(token => token.chainId === chainId);
22 | }
23 | }
24 | }
25 | } catch (defaultTokensError) {
26 | logError({ defaultTokensError });
27 | }
28 | return [];
29 | };
30 |
31 | const homeTokensQuery = gql`
32 | query homeTokens {
33 | tokens(where: { homeAddress_contains: "0x" }, first: 1000) {
34 | chainId: homeChainId
35 | address: homeAddress
36 | name: homeName
37 | symbol
38 | decimals
39 | }
40 | }
41 | `;
42 |
43 | const foreignTokensQuery = gql`
44 | query foreignTokens {
45 | tokens(where: { foreignAddress_contains: "0x" }, first: 1000) {
46 | chainId: foreignChainId
47 | address: foreignAddress
48 | name: foreignName
49 | symbol
50 | decimals
51 | }
52 | }
53 | `;
54 |
55 | const fetchTokensFromSubgraph = async (homeEndpoint, foreignEndpoint) => {
56 | try {
57 | const [homeData, foreignData] = await Promise.all([
58 | request(homeEndpoint, homeTokensQuery),
59 | request(foreignEndpoint, foreignTokensQuery),
60 | ]);
61 | const homeTokens = homeData && homeData.tokens ? homeData.tokens : [];
62 | const foreignTokens =
63 | foreignData && foreignData.tokens ? foreignData.tokens : [];
64 | return homeTokens.concat(foreignTokens);
65 | } catch (subgraphTokensError) {
66 | logError({ subgraphTokensError });
67 | }
68 | return [];
69 | };
70 |
71 | export function memoize(method) {
72 | const cache = {};
73 |
74 | return async function memoized(...args) {
75 | const argString = JSON.stringify(args);
76 | cache[argString] = cache[argString] || (await method(...args));
77 | return cache[argString];
78 | };
79 | }
80 |
81 | export const fetchTokenList = memoize(
82 | async (chainId, homeEndpoint, foreignEndpoint) => {
83 | const [defaultTokens, subgraphTokens] = await Promise.all([
84 | fetchDefaultTokens(chainId),
85 | fetchTokensFromSubgraph(homeEndpoint, foreignEndpoint),
86 | ]);
87 | const tokens = uniqueTokens(defaultTokens.concat(subgraphTokens));
88 | return tokens.map(token => ({
89 | ...token,
90 | name: renamexDaiTokensAsGnosis(token.name),
91 | }));
92 | },
93 | );
94 |
--------------------------------------------------------------------------------
/packages/dapp/src/pages/History.jsx:
--------------------------------------------------------------------------------
1 | import { Header } from 'components/common/Header';
2 | import { BridgeHistory } from 'components/history/BridgeHistory';
3 | import query from 'query-string';
4 | import React from 'react';
5 |
6 | export const History = ({ location }) => {
7 | const parsed = query.parse(location.search);
8 | const page = parseInt(parsed.page, 10);
9 | const pageNumber = isNaN(page) || page <= 0 ? 1 : page;
10 | return (
11 | <>
12 |
13 |
14 | >
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/packages/dapp/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import { BridgeTokens } from 'components/bridge/BridgeTokens';
2 | import { Header } from 'components/common/Header';
3 | import { BridgeProvider } from 'contexts/BridgeContext';
4 | import React from 'react';
5 |
6 | export const Home = () => (
7 |
8 |
9 |
10 |
11 | );
12 |
--------------------------------------------------------------------------------
/packages/dapp/src/stores/ethPrice.js:
--------------------------------------------------------------------------------
1 | import { logDebug, logError } from 'lib/helpers';
2 |
3 | const ethPriceFromApi = async fetchFn => {
4 | try {
5 | const response = await fetchFn();
6 | const json = await response.json();
7 | const oracleEthPrice = json.ethereum.usd;
8 |
9 | if (!oracleEthPrice) {
10 | logError(`Response from Oracle didn't include eth price`);
11 | return null;
12 | }
13 |
14 | logDebug('Updated ETH Price', json.ethereum);
15 |
16 | return oracleEthPrice;
17 | } catch (e) {
18 | logError(`ETH Price API is not available. ${e.message}`);
19 | }
20 | return null;
21 | };
22 |
23 | const { REACT_APP_ETH_PRICE_API_URL, REACT_APP_ETH_PRICE_UPDATE_INTERVAL } =
24 | process.env;
25 |
26 | const DEFAULT_ETH_PRICE_API_URL =
27 | 'https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=USD';
28 | const DEFAULT_ETH_PRICE_UPDATE_INTERVAL = 60000;
29 |
30 | class EthPriceStore {
31 | ethPrice = null;
32 |
33 | ethPriceApiUrl = null;
34 |
35 | updateInterval = null;
36 |
37 | constructor() {
38 | this.ethPriceApiUrl =
39 | REACT_APP_ETH_PRICE_API_URL || DEFAULT_ETH_PRICE_API_URL;
40 | this.updateInterval =
41 | REACT_APP_ETH_PRICE_UPDATE_INTERVAL || DEFAULT_ETH_PRICE_UPDATE_INTERVAL;
42 | this.updateGasPrice();
43 | }
44 |
45 | async updateGasPrice() {
46 | const fetchFn = () => fetch(this.ethPriceApiUrl);
47 | this.ethPrice = await ethPriceFromApi(fetchFn);
48 | setTimeout(() => this.updateGasPrice(), this.updateInterval);
49 | }
50 |
51 | ethPriceInUSD() {
52 | return this.ethPrice;
53 | }
54 | }
55 |
56 | const ethPriceStore = new EthPriceStore();
57 |
58 | export const getETHPrice = () => ethPriceStore.ethPriceInUSD();
59 |
--------------------------------------------------------------------------------
/packages/dapp/src/stores/graphHealth.js:
--------------------------------------------------------------------------------
1 | import { getHealthStatus } from 'lib/graphHealth';
2 | import { logDebug } from 'lib/helpers';
3 | import { networks } from 'lib/networks';
4 |
5 | const subgraphs = [];
6 | Object.values(networks).forEach(info => {
7 | subgraphs.push(info.homeGraphName);
8 | subgraphs.push(info.foreignGraphName);
9 | });
10 |
11 | const { REACT_APP_GRAPH_HEALTH_UPDATE_INTERVAL } = process.env;
12 |
13 | const DEFAULT_GRAPH_HEALTH_UPDATE_INTERVAL = 60000;
14 |
15 | const UPDATE_INTERVAL =
16 | REACT_APP_GRAPH_HEALTH_UPDATE_INTERVAL ||
17 | DEFAULT_GRAPH_HEALTH_UPDATE_INTERVAL;
18 |
19 | class GraphHealthStore {
20 | graphHealth = {};
21 |
22 | constructor() {
23 | this.updateGraphHealth();
24 | }
25 |
26 | async updateGraphHealth() {
27 | await Promise.all(
28 | subgraphs.map(async subgraph => {
29 | const status = await getHealthStatus(subgraph);
30 | this.graphHealth[subgraph] = status;
31 | }),
32 | );
33 | logDebug('Updated Graph Health', this.graphHealth);
34 | setTimeout(() => this.updateGraphHealth(), UPDATE_INTERVAL);
35 | }
36 |
37 | status() {
38 | return this.graphHealth;
39 | }
40 | }
41 |
42 | const graphHealthStore = new GraphHealthStore();
43 |
44 | export const getGraphHealth = () => graphHealthStore.status();
45 |
--------------------------------------------------------------------------------
/packages/dapp/src/stores/rpcHealth.js:
--------------------------------------------------------------------------------
1 | import { chainUrls } from 'lib/constants';
2 | import { logDebug, logError } from 'lib/helpers';
3 | import { getValidEthersProvider } from 'lib/providerHelpers';
4 |
5 | const { REACT_APP_RPC_HEALTH_UPDATE_INTERVAL } = process.env;
6 |
7 | const DEFAULT_RPC_HEALTH_UPDATE_INTERVAL = 60000;
8 |
9 | const UPDATE_INTERVAL =
10 | REACT_APP_RPC_HEALTH_UPDATE_INTERVAL || DEFAULT_RPC_HEALTH_UPDATE_INTERVAL;
11 |
12 | class RPCHealthStore {
13 | rpcHealth = {};
14 |
15 | constructor() {
16 | setTimeout(() => this.updateRPCHealth(), 5000);
17 | }
18 |
19 | async updateRPCHealth() {
20 | await Promise.all(
21 | Object.entries(chainUrls).map(async ([chainId, { name }]) => {
22 | try {
23 | const provider = await getValidEthersProvider(chainId);
24 | this.rpcHealth[chainId] = provider
25 | ? await provider.getBlockNumber()
26 | : false;
27 | } catch (error) {
28 | this.rpcHealth[chainId] = false;
29 | logError(`${name} RPC Health Error: `, error);
30 | }
31 | }),
32 | );
33 | logDebug('Updated RPC Health', this.rpcHealth);
34 | setTimeout(() => this.updateRPCHealth(), UPDATE_INTERVAL);
35 | }
36 |
37 | status() {
38 | return this.rpcHealth;
39 | }
40 |
41 | setHealth(chainId, blockNumber) {
42 | this.rpcHealth[chainId] = blockNumber;
43 | }
44 | }
45 |
46 | const rpcHealthStore = new RPCHealthStore();
47 |
48 | export const getRPCHealth = () => rpcHealthStore.status();
49 |
50 | export const setRPCHealth = (chainId, blockNumber) => {
51 | rpcHealthStore.setHealth(chainId, blockNumber);
52 | };
53 |
--------------------------------------------------------------------------------
/packages/dapp/src/theme.js:
--------------------------------------------------------------------------------
1 | import { extendTheme } from '@chakra-ui/react';
2 |
3 | export const theme = extendTheme({
4 | breakpoints: {
5 | base: '0em',
6 | sm: '30em',
7 | md: '48em',
8 | lg: '62em',
9 | xl: '70em',
10 | '2xl': '80em',
11 | '3xl': '96em',
12 | },
13 | colors: {
14 | blue: {
15 | 50: '#edf9ff',
16 | 100: '#ddf3ff',
17 | 200: '#afd7ff',
18 | 300: '#7ebcff',
19 | 400: '#4da1ff',
20 | 500: '#2086fe',
21 | 600: '#0a6ce5',
22 | 700: '#0054b3',
23 | 800: '#003c81',
24 | 900: '#002450',
25 | },
26 | cyan: {
27 | 50: '#edfcff',
28 | 100: '#dcfaff',
29 | 200: '#b4eafb',
30 | 300: '#89daf4',
31 | 400: '#5dccee',
32 | 500: '#36bde8',
33 | 600: '#20a4cf',
34 | 700: '#117fa2',
35 | 800: '#035b75',
36 | 900: '#003848',
37 | },
38 | red: {
39 | 50: '#ffe5e5',
40 | 100: '#fbb8b8',
41 | 200: '#f48a8a',
42 | 300: '#f48a8a',
43 | 400: '#f48a8a',
44 | 500: '#ef5d5d',
45 | 600: '#e9302e',
46 | 700: '#750b0b',
47 | 800: '#470505',
48 | 900: '#1d0000',
49 | },
50 | purple: {
51 | 50: '#f0e4ff',
52 | 100: '#cbb2ff',
53 | 200: '#a880ff',
54 | 300: '#864dff',
55 | 400: '#631bfe',
56 | 500: '#4a02e5',
57 | 600: '#3900b3',
58 | 700: '#290081',
59 | 800: '#17004f',
60 | 900: '#09001f',
61 | },
62 | grey: '#A0B6D7',
63 | background: '#EEF4FD',
64 | lightBackground: '#F2F6FB',
65 | greyText: '#75818D',
66 | modalBG: 'rgba(98, 118, 148, 0.9)',
67 | modalOpaqueBG: 'rgba(98, 118, 148, 1)',
68 | },
69 | fonts: {
70 | body: 'Roboto, sans-serif',
71 | },
72 | });
73 |
--------------------------------------------------------------------------------
/packages/subgraph/config/bsc-mainnet.json:
--------------------------------------------------------------------------------
1 | {
2 | "network": "bsc",
3 | "startBlock": "7471226",
4 | "omnibridge": "0xD83893F31AA1B6B9D97C9c70D3492fe38D24d218",
5 | "amb": "0x6943A218d58135793F1FE619414eD476C37ad65a"
6 | }
7 |
--------------------------------------------------------------------------------
/packages/subgraph/config/bsc-xdai.json:
--------------------------------------------------------------------------------
1 | {
2 | "network": "bsc",
3 | "startBlock": "4792263",
4 | "omnibridge": "0xF0b456250DC9990662a6F25808cC74A6d1131Ea9",
5 | "amb": "0x05185872898b6f94AA600177EF41B9334B1FA48B"
6 | }
7 |
--------------------------------------------------------------------------------
/packages/subgraph/config/kovan.json:
--------------------------------------------------------------------------------
1 | {
2 | "network": "kovan",
3 | "startBlock": "20007099",
4 | "omnibridge": "0xa960d095470f7509955d5402e36d9db984b5c8e2",
5 | "amb": "0xfe446bef1dbf7afe24e81e05bc8b271c1ba9a560",
6 | "bridges": [
7 | {
8 | "name": "DEMO2712",
9 | "address": "0xA68Bd659A9167F3D3C01bA9776A1208dae8F003b",
10 | "startBlock": "23183142"
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/subgraph/config/mainnet-bsc.json:
--------------------------------------------------------------------------------
1 | {
2 | "network": "mainnet",
3 | "startBlock": "11375600",
4 | "omnibridge": "0x69c707d975e8d883920003CC357E556a4732CD03",
5 | "amb": "0x07955be2967B655Cf52751fCE7ccC8c61EA594e2"
6 | }
7 |
--------------------------------------------------------------------------------
/packages/subgraph/config/mainnet.json:
--------------------------------------------------------------------------------
1 | {
2 | "network": "mainnet",
3 | "startBlock": "10590093",
4 | "omnibridge": "0x88ad09518695c6c3712AC10a214bE5109a655671",
5 | "amb": "0x4C36d2919e407f0Cc2Ee3c993ccF8ac26d9CE64e",
6 | "bridges": [
7 | {
8 | "name": "OWL",
9 | "address": "0xed7e6720Ac8525Ac1AEee710f08789D02cD87ecB",
10 | "startBlock": "10840608"
11 | },
12 | {
13 | "name": "MOON",
14 | "address": "0xE7228B4EBAD37Ba031a8b63473727f991e262dCd",
15 | "startBlock": "11753831"
16 | },
17 | {
18 | "name": "HNY",
19 | "address": "0x81A4833B3A40E7c61eFE9D1a287343797993B1E8",
20 | "startBlock": "11761811"
21 | },
22 | {
23 | "name": "XDATA",
24 | "address": "0x2eeeDdeECe91c9F4c5bA4C8E1d784A0234C6d015",
25 | "startBlock": "12004477"
26 | },
27 | {
28 | "name": "DATA",
29 | "address": "0x29e572d45cC33D5a68DCc8f92bfc7ded0017Bc59",
30 | "startBlock": "13148806"
31 | },
32 | {
33 | "name": "AGVE",
34 | "address": "0x5689C65cfe5E8BF1A5F836c956DeA1b3B8BE00Bb",
35 | "startBlock": "12624668"
36 | },
37 | {
38 | "name": "SWASH",
39 | "address": "0xE964A36142BbE39751D0B4D6140fC0b8c48e68bE",
40 | "startBlock": "13443803"
41 | },
42 | {
43 | "name": "UDT",
44 | "address": "0x41a4ee2855A7Dc328524babB07d7f505B201133e",
45 | "startBlock": "13708426"
46 | }
47 | ]
48 | }
49 |
--------------------------------------------------------------------------------
/packages/subgraph/config/poa-xdai.json:
--------------------------------------------------------------------------------
1 | {
2 | "network": "poa-core",
3 | "startBlock": "23282921",
4 | "omnibridge": "0x8134470b7CF6f57Faee2076adf8F7301fD5865a5",
5 | "amb": "0xB2218bdEbe8e90f80D04286772B0968ead666942"
6 | }
7 |
--------------------------------------------------------------------------------
/packages/subgraph/config/sokol.json:
--------------------------------------------------------------------------------
1 | {
2 | "network": "poa-sokol",
3 | "startBlock": "16064799",
4 | "omnibridge": "0x40CdfF886715A4012fAD0219D15C98bB149AeF0e",
5 | "amb": "0xFe446bEF1DbF7AFE24E81e05BC8B271C1BA9a560",
6 | "bridges": [
7 | {
8 | "name": "DEMO2712",
9 | "address": "0x2a5fc52d8A563B2F181c6A527D422e1592C9ecFa",
10 | "startBlock": "19084051"
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/subgraph/config/xdai-bsc.json:
--------------------------------------------------------------------------------
1 | {
2 | "network": "xdai",
3 | "startBlock": "14496725",
4 | "omnibridge": "0x59447362798334d3485c64D1e4870Fde2DDC0d75",
5 | "amb": "0x162E898bD0aacB578C8D5F8d6ca588c13d2A383F"
6 | }
7 |
--------------------------------------------------------------------------------
/packages/subgraph/config/xdai-poa.json:
--------------------------------------------------------------------------------
1 | {
2 | "network": "xdai",
3 | "startBlock": "18102096",
4 | "omnibridge": "0x63be59CF177cA9bb317DE8C4aa965Ddda93CB9d7",
5 | "amb": "0xc2d77d118326c33BBe36EbeAbf4F7ED6BC2dda5c"
6 | }
7 |
--------------------------------------------------------------------------------
/packages/subgraph/config/xdai.json:
--------------------------------------------------------------------------------
1 | {
2 | "network": "xdai",
3 | "startBlock": "11300566",
4 | "omnibridge": "0xf6A78083ca3e2a662D6dd1703c939c8aCE2e268d",
5 | "amb": "0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59",
6 | "bridges": [
7 | {
8 | "name": "OWL",
9 | "address": "0xbeD794745e2a0543eE609795ade87A55Bbe935Ba",
10 | "startBlock": "11948121"
11 | },
12 | {
13 | "name": "MOON",
14 | "address": "0xF75C28fE07E0647B05160288F172ad27CccD8f30",
15 | "startBlock": "14285411"
16 | },
17 | {
18 | "name": "HNY",
19 | "address": "0x0EeAcdb0Dd96588711581C5f3173dD55841b8e91",
20 | "startBlock": "14306413"
21 | },
22 | {
23 | "name": "XDATA",
24 | "address": "0x7d55f9981d4E10A193314E001b96f72FCc901e40",
25 | "startBlock": "14923817"
26 | },
27 | {
28 | "name": "DATA",
29 | "address": "0x53f3F44c434494da73EC44a6E8a8D091332bC2ce",
30 | "startBlock": "17894604"
31 | },
32 | {
33 | "name": "AGVE",
34 | "address": "0xBE20F60339b06Db32C319d46cf3Bc9bAcC0694aB",
35 | "startBlock": "16549014"
36 | },
37 | {
38 | "name": "SWASH",
39 | "address": "0x68a64df7458a8eb2677991e657508fe00205332d",
40 | "startBlock": "18638013"
41 | },
42 | {
43 | "name": "UDT",
44 | "address": "0x5F0FE58709639A39c193521d919aFaef02E570F7",
45 | "startBlock": "19318491"
46 | }
47 | ]
48 | }
49 |
--------------------------------------------------------------------------------
/packages/subgraph/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@omnibridge/subgraph",
3 | "version": "1.0.0",
4 | "dependencies": {
5 | "@graphprotocol/graph-cli": "^0.21.1",
6 | "@graphprotocol/graph-ts": "^0.20.1"
7 | },
8 | "license": "MIT",
9 | "scripts": {
10 | "auth": "graph auth https://api.thegraph.com/deploy/ $GRAPH_ACCESS_TOKEN",
11 | "prepare-mainnet": "mustache config/mainnet.json subgraph.template.yaml > subgraph.yaml",
12 | "prepare-xdai": "mustache config/xdai.json subgraph.template.yaml > subgraph.yaml",
13 | "prepare-kovan": "mustache config/kovan.json subgraph.template.yaml > subgraph.yaml",
14 | "prepare-sokol": "mustache config/sokol.json subgraph.template.yaml > subgraph.yaml",
15 | "prepare-xdai-bsc": "mustache config/xdai-bsc.json subgraph.template.yaml > subgraph.yaml",
16 | "prepare-bsc-xdai": "mustache config/bsc-xdai.json subgraph.template.yaml > subgraph.yaml",
17 | "prepare-xdai-poa": "mustache config/xdai-poa.json subgraph.template.yaml > subgraph.yaml",
18 | "prepare-poa-xdai": "mustache config/poa-xdai.json subgraph.template.yaml > subgraph.yaml",
19 | "prepare-mainnet-bsc": "mustache config/mainnet-bsc.json subgraph.template.yaml > subgraph.yaml",
20 | "prepare-bsc-mainnet": "mustache config/bsc-mainnet.json subgraph.template.yaml > subgraph.yaml",
21 | "codegen": "graph codegen --output-dir src/types/",
22 | "build": "graph build",
23 | "deploy-mainnet": "graph deploy raid-guild/mainnet-omnibridge --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/",
24 | "deploy-xdai": "graph deploy raid-guild/xdai-omnibridge --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/",
25 | "deploy-kovan": "graph deploy dan13ram/kovan-omnibridge --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/",
26 | "deploy-sokol": "graph deploy dan13ram/sokol-omnibridge --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/",
27 | "deploy-xdai-bsc": "graph deploy dan13ram/xdai-to-bsc-omnibridge --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/",
28 | "deploy-bsc-xdai": "graph deploy dan13ram/bsc-to-xdai-omnibridge --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/",
29 | "deploy-xdai-poa": "graph deploy dan13ram/xdai-to-poa-omnibridge --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/",
30 | "deploy-poa-xdai": "graph deploy dan13ram/poa-to-xdai-omnibridge --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/",
31 | "deploy-mainnet-bsc": "graph deploy dan13ram/mainnet-to-bsc-omnibridge --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/",
32 | "deploy-bsc-mainnet": "graph deploy dan13ram/bsc-to-mainnet-omnibridge --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/"
33 | },
34 | "devDependencies": {
35 | "mustache": "^4.2.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/subgraph/schema.graphql:
--------------------------------------------------------------------------------
1 | type UserRequest @entity {
2 | id: ID!
3 | user: Bytes
4 | token: Bytes
5 | decimals: Int
6 | symbol: String
7 | amount: BigInt
8 | timestamp: BigInt!
9 | txHash: Bytes!
10 | messageId: Bytes
11 | encodedData: Bytes
12 | message: Message
13 | recipient: Bytes
14 | }
15 |
16 | type Execution @entity {
17 | id: ID!
18 | user: Bytes
19 | token: Bytes
20 | amount: BigInt
21 | sender: Bytes
22 | executor: Bytes
23 | messageId: Bytes
24 | status: Boolean
25 | timestamp: BigInt!
26 | txHash: Bytes!
27 | }
28 |
29 | type Message @entity {
30 | id: ID!
31 | msgId: Bytes
32 | txHash: Bytes!
33 | msgHash: Bytes
34 | msgData: Bytes
35 | signatures: [Bytes!]
36 | }
37 |
38 | type Token @entity {
39 | id: ID!
40 | homeAddress: Bytes!
41 | homeName: String!
42 | homeChainId: Int!
43 | decimals: Int!
44 | symbol: String!
45 | foreignAddress: Bytes
46 | foreignName: String
47 | foreignChainId: Int
48 | }
49 |
--------------------------------------------------------------------------------
/packages/subgraph/src/abis/amb.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "anonymous": false,
4 | "inputs": [
5 | { "indexed": true, "name": "messageId", "type": "bytes32" },
6 | { "indexed": false, "name": "encodedData", "type": "bytes" }
7 | ],
8 | "name": "UserRequestForAffirmation",
9 | "type": "event"
10 | },
11 | {
12 | "anonymous": false,
13 | "inputs": [
14 | { "indexed": true, "name": "messageId", "type": "bytes32" },
15 | { "indexed": false, "name": "encodedData", "type": "bytes" }
16 | ],
17 | "name": "UserRequestForSignature",
18 | "type": "event"
19 | },
20 | {
21 | "anonymous": false,
22 | "inputs": [
23 | { "indexed": true, "name": "sender", "type": "address" },
24 | { "indexed": true, "name": "executor", "type": "address" },
25 | { "indexed": true, "name": "messageId", "type": "bytes32" },
26 | { "indexed": false, "name": "status", "type": "bool" }
27 | ],
28 | "name": "RelayedMessage",
29 | "type": "event"
30 | },
31 | {
32 | "anonymous": false,
33 | "inputs": [
34 | { "indexed": true, "name": "sender", "type": "address" },
35 | { "indexed": true, "name": "executor", "type": "address" },
36 | { "indexed": true, "name": "messageId", "type": "bytes32" },
37 | { "indexed": false, "name": "status", "type": "bool" }
38 | ],
39 | "name": "AffirmationCompleted",
40 | "type": "event"
41 | },
42 | {
43 | "anonymous": false,
44 | "inputs": [
45 | { "indexed": true, "name": "signer", "type": "address" },
46 | { "indexed": false, "name": "messageHash", "type": "bytes32" }
47 | ],
48 | "name": "SignedForUserRequest",
49 | "type": "event"
50 | },
51 | {
52 | "type": "event",
53 | "name": "CollectedSignatures",
54 | "inputs": [
55 | {
56 | "type": "address",
57 | "name": "authorityResponsibleForRelay",
58 | "indexed": false
59 | },
60 | { "type": "bytes32", "name": "messageHash", "indexed": false },
61 | {
62 | "type": "uint256",
63 | "name": "NumberOfCollectedSignatures",
64 | "indexed": false
65 | }
66 | ],
67 | "anonymous": false
68 | },
69 | {
70 | "type": "function",
71 | "stateMutability": "view",
72 | "payable": false,
73 | "outputs": [{ "type": "bytes", "name": "" }],
74 | "name": "signature",
75 | "inputs": [
76 | { "type": "bytes32", "name": "_hash" },
77 | { "type": "uint256", "name": "_index" }
78 | ],
79 | "constant": true
80 | },
81 | {
82 | "type": "function",
83 | "stateMutability": "view",
84 | "payable": false,
85 | "outputs": [{ "type": "bytes", "name": "" }],
86 | "name": "message",
87 | "inputs": [{ "type": "bytes32", "name": "_hash" }],
88 | "constant": true
89 | }
90 | ]
91 |
--------------------------------------------------------------------------------
/packages/subgraph/src/abis/bridge.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "anonymous": false,
4 | "inputs": [
5 | { "indexed": true, "name": "recipient", "type": "address" },
6 | { "indexed": false, "name": "value", "type": "uint256" },
7 | { "indexed": true, "name": "messageId", "type": "bytes32" }
8 | ],
9 | "name": "TokensBridged",
10 | "type": "event"
11 | },
12 | {
13 | "anonymous": false,
14 | "inputs": [
15 | { "indexed": true, "name": "sender", "type": "address" },
16 | { "indexed": false, "name": "value", "type": "uint256" },
17 | { "indexed": true, "name": "messageId", "type": "bytes32" }
18 | ],
19 | "name": "TokensBridgingInitiated",
20 | "type": "event"
21 | }
22 | ]
23 |
--------------------------------------------------------------------------------
/packages/subgraph/src/abis/omnibridge.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "anonymous": false,
4 | "inputs": [
5 | { "indexed": true, "name": "token", "type": "address" },
6 | { "indexed": true, "name": "recipient", "type": "address" },
7 | { "indexed": false, "name": "value", "type": "uint256" },
8 | { "indexed": true, "name": "messageId", "type": "bytes32" }
9 | ],
10 | "name": "TokensBridged",
11 | "type": "event"
12 | },
13 | {
14 | "anonymous": false,
15 | "inputs": [
16 | { "indexed": true, "name": "token", "type": "address" },
17 | { "indexed": true, "name": "sender", "type": "address" },
18 | { "indexed": false, "name": "value", "type": "uint256" },
19 | { "indexed": true, "name": "messageId", "type": "bytes32" }
20 | ],
21 | "name": "TokensBridgingInitiated",
22 | "type": "event"
23 | },
24 | {
25 | "type": "event",
26 | "name": "NewTokenRegistered",
27 | "inputs": [
28 | {
29 | "type": "address",
30 | "name": "foreignToken",
31 | "indexed": true
32 | },
33 | {
34 | "type": "address",
35 | "name": "homeToken",
36 | "indexed": true
37 | }
38 | ],
39 | "anonymous": false
40 | }
41 | ]
42 |
--------------------------------------------------------------------------------
/packages/subgraph/src/abis/token.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "function",
4 | "stateMutability": "view",
5 | "payable": false,
6 | "outputs": [
7 | {
8 | "type": "string",
9 | "name": ""
10 | }
11 | ],
12 | "name": "name",
13 | "inputs": [],
14 | "constant": true
15 | },
16 | {
17 | "type": "function",
18 | "stateMutability": "view",
19 | "payable": false,
20 | "outputs": [
21 | {
22 | "type": "string",
23 | "name": ""
24 | }
25 | ],
26 | "name": "symbol",
27 | "inputs": [],
28 | "constant": true
29 | },
30 | {
31 | "type": "function",
32 | "stateMutability": "view",
33 | "payable": false,
34 | "outputs": [
35 | {
36 | "type": "uint8",
37 | "name": ""
38 | }
39 | ],
40 | "name": "decimals",
41 | "inputs": [],
42 | "cstant": true
43 | },
44 | {
45 | "type": "event",
46 | "name": "Transfer",
47 | "inputs": [
48 | {
49 | "type": "address",
50 | "name": "from",
51 | "indexed": true
52 | },
53 | {
54 | "type": "address",
55 | "name": "to",
56 | "indexed": true
57 | },
58 | {
59 | "type": "uint256",
60 | "name": "value",
61 | "indexed": false
62 | }
63 | ],
64 | "anonymous": false
65 | }
66 | ]
67 |
--------------------------------------------------------------------------------
/packages/subgraph/src/mappings/bridge.ts:
--------------------------------------------------------------------------------
1 | import { log, Address } from '@graphprotocol/graph-ts';
2 | import {
3 | TokensBridgingInitiated,
4 | TokensBridged,
5 | } from '../types/Omnibridge/Bridge';
6 | import { Execution, UserRequest } from '../types/schema';
7 |
8 | import {
9 | fetchTokenInfo,
10 | updateHomeTokenInfo,
11 | updateHomeToken,
12 | } from './helpers';
13 |
14 | import { getMediatedTokens } from './overrides';
15 |
16 | export function handleBridgeTransfer(event: TokensBridged): void {
17 | log.debug('Parsing TokensBridged for txHash {}', [
18 | event.transaction.hash.toHexString(),
19 | ]);
20 | let mediatedTokens = getMediatedTokens();
21 | if (!mediatedTokens.isSet(event.address)) return;
22 | let txHash = event.transaction.hash;
23 | let execution = Execution.load(txHash.toHexString());
24 | if (execution == null) {
25 | execution = new Execution(txHash.toHexString());
26 | }
27 | execution.txHash = txHash;
28 | execution.timestamp = event.block.timestamp;
29 | let tokenAddress = mediatedTokens.get(event.address) as Address;
30 | execution.token = tokenAddress;
31 | execution.user = event.params.recipient;
32 | execution.amount = event.params.value;
33 | execution.messageId = event.params.messageId;
34 | execution.save();
35 |
36 | log.debug('TokensBridged token {}', [execution.token.toHexString()]);
37 |
38 | updateHomeToken(tokenAddress);
39 | }
40 |
41 | export function handleInitiateTransfer(event: TokensBridgingInitiated): void {
42 | log.debug('Parsing TokensBridgingInitiated for txHash {}', [
43 | event.transaction.hash.toHexString(),
44 | ]);
45 | let mediatedTokens = getMediatedTokens();
46 | if (!mediatedTokens.isSet(event.address)) return;
47 | let txHash = event.transaction.hash;
48 | let request = UserRequest.load(txHash.toHexString());
49 | if (request == null) {
50 | request = new UserRequest(txHash.toHexString());
51 | }
52 | request.txHash = txHash;
53 | request.timestamp = event.block.timestamp;
54 | let tokenAddress = mediatedTokens.get(event.address) as Address;
55 | request.token = tokenAddress;
56 | let tokenInfo = fetchTokenInfo(tokenAddress);
57 | request.decimals = tokenInfo.decimals;
58 | request.symbol = tokenInfo.symbol;
59 | request.user = event.params.sender;
60 | request.amount = event.params.value;
61 | request.messageId = event.params.messageId;
62 | request.save();
63 |
64 | log.debug('TokensBridgingInitiated token {}', [request.token.toHexString()]);
65 |
66 | updateHomeTokenInfo(tokenAddress, tokenInfo);
67 | }
68 |
--------------------------------------------------------------------------------