├── .env.example
├── .env.test
├── .github
├── actions
│ └── setup
│ │ └── action.yaml
├── dependabot.yml
└── workflows
│ ├── chromatic.yaml
│ ├── dependency-review.yaml
│ ├── e2e.yaml
│ └── lint-test.yaml
├── .gitignore
├── .husky
└── pre-commit
├── .nvmrc
├── .prettierrc
├── .storybook
├── main.ts
└── preview.tsx
├── CODEOWNERS
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── api-docs-openapi.yaml
├── api
├── _abis.ts
├── _adapters
│ └── vercel-adapter.ts
├── _address.ts
├── _auth.ts
├── _base
│ ├── api-adapter.ts
│ └── api-handler.ts
├── _cache.ts
├── _constants.ts
├── _dexes
│ ├── 1inch.ts
│ ├── cross-swap-service.ts
│ ├── gho
│ │ ├── multicall.ts
│ │ ├── utils
│ │ │ └── wgho.ts
│ │ └── wrapped-gho.ts
│ ├── types.ts
│ ├── uniswap
│ │ ├── swap-quoter.ts
│ │ ├── swap-router-02.ts
│ │ ├── universal-router.ts
│ │ └── utils
│ │ │ ├── adapter.ts
│ │ │ ├── addresses.ts
│ │ │ ├── conversion.ts
│ │ │ ├── tokens.ts
│ │ │ ├── trading-api.ts
│ │ │ └── v3-sdk.ts
│ └── utils.ts
├── _eip712.ts
├── _env.ts
├── _erc20.ts
├── _errors.ts
├── _exclusivity
│ ├── config.ts
│ ├── index.ts
│ ├── strategies
│ │ ├── index.ts
│ │ └── weighted-random.ts
│ └── types.ts
├── _fill-deadline.ts
├── _integrator-id.ts
├── _multicall-handler.ts
├── _permit.ts
├── _relayer-address.ts
├── _spoke-pool-periphery.ts
├── _swap-and-bridge.ts
├── _timings.ts
├── _transfer-with-auth.ts
├── _typechain
│ ├── SpokePoolPeripheryProxy.ts
│ ├── SpokePoolV3Periphery.ts
│ └── factories
│ │ ├── SpokePoolPeripheryProxy__factory.ts
│ │ └── SpokePoolV3Periphery__factory.ts
├── _typeguards.ts
├── _types
│ ├── generic.types.ts
│ ├── index.ts
│ └── utility.types.ts
├── _utils.ts
├── account-balance.ts
├── available-routes.ts
├── batch-account-balance.ts
├── build-deposit-tx.ts
├── chains.ts
├── coingecko.ts
├── cron-cache-balances.ts
├── cron-cache-gas-costs.ts
├── cron-cache-gas-prices.ts
├── cron-cache-l1-data-fee.ts
├── cron-cache-l1-token-configs.ts
├── cron-ping-endpoints.ts
├── gas-prices.ts
├── limits.ts
├── liquid-reserves.ts
├── pools-list.ts
├── pools-user.ts
├── pools.ts
├── rpc-proxy.ts
├── suggested-fees.ts
├── swap-quote.ts
├── swap
│ ├── _utils.ts
│ ├── approval
│ │ ├── _service.ts
│ │ ├── _utils.ts
│ │ └── index.ts
│ └── index.ts
└── token-list.ts
├── e2e
├── config.ts
├── synpress.ts
├── tests
│ ├── 00_bridge_deposit.spec.ts
│ ├── 01_bridge_page.spec.ts
│ ├── 02_pool_page.spec.ts
│ ├── 03_rewards_page.spec.ts
│ └── 04_transactions_page.spec.ts
├── utils
│ ├── bridge-page.ts
│ └── deposit-test-routes.ts
└── wallet-setup
│ ├── basic.setup.ts
│ └── connected.setup.ts
├── eslint.config.mjs
├── funding.json
├── index.html
├── jest.config.cjs
├── netlify.toml
├── package.json
├── patches
└── @balancer-labs+sdk+1.1.6-beta.16.patch
├── playwright.config.ts
├── public
├── favicon.svg
├── fonts
│ ├── Barlow-Bold.woff2
│ ├── Barlow-Medium.woff2
│ └── Barlow-Regular.woff2
├── logo-small.png
├── robots.txt
├── sitemap.xml
└── thumbnail.png
├── scripts
├── README.md
├── chain-configs
│ ├── aleph-zero
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── arbitrum-sepolia
│ │ └── index.ts
│ ├── arbitrum
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── base-sepolia
│ │ └── index.ts
│ ├── base
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── blast-sepolia
│ │ └── index.ts
│ ├── blast
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── bsc
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── index.ts
│ ├── ink
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── lens-sepolia
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── lens
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── linea
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── lisk-sepolia
│ │ └── index.ts
│ ├── lisk
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── mainnet
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── mode-sepolia
│ │ └── index.ts
│ ├── mode
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── optimism-sepolia
│ │ └── index.ts
│ ├── optimism
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── polygon-amoy
│ │ └── index.ts
│ ├── polygon
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── redstone
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── scroll
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── sepolia
│ │ └── index.ts
│ ├── soneium
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── tatara
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── types.ts
│ ├── unichain-sepolia
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── unichain
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── world-chain
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── zk-sync
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ └── zora
│ │ ├── assets
│ │ ├── grayscale-logo.svg
│ │ └── logo.svg
│ │ └── index.ts
├── extern-configs
│ ├── hyperliquid
│ │ ├── assets
│ │ │ ├── grayscale-logo.svg
│ │ │ └── logo.svg
│ │ └── index.ts
│ ├── index.ts
│ └── types.ts
├── fetch-remote-config.ts
├── fetch-remote-env.ts
├── gas-prices.ts
├── generate-chain-config.ts
├── generate-e2e-test-routes.ts
├── generate-routes.ts
├── generate-swap-routes.ts
├── generate-ui-assets.ts
├── remote-configs
│ └── utils.ts
└── tests
│ ├── _swap-cases.ts
│ ├── _swap-utils.ts
│ ├── swap-allowance.ts
│ ├── swap-auth.ts
│ ├── swap-permit.ts
│ └── swap-unified.ts
├── setup.jest.ts
├── src
├── App.tsx
├── Routes.tsx
├── ampli
│ └── index.ts
├── assets
│ ├── bg-banners
│ │ ├── action-card-aqua-banner.svg
│ │ ├── action-card-teal-banner.svg
│ │ ├── arb-cloud-rebate.svg
│ │ ├── blue-card-banner.svg
│ │ ├── cloud-staking.svg
│ │ ├── default-banner.svg
│ │ ├── deposit-banner.svg
│ │ ├── empty-deposits-banner.svg
│ │ ├── green-card-banner.svg
│ │ ├── op-cloud-rebate.svg
│ │ ├── overview-card-background.svg
│ │ ├── purple-card-banner.svg
│ │ ├── stars-bg.png
│ │ └── transparent-banner.svg
│ ├── chain-logos
│ │ ├── aleph-zero-grayscale.svg
│ │ ├── aleph-zero.svg
│ │ ├── arbitrum-grayscale.svg
│ │ ├── arbitrum-sepolia-grayscale.svg
│ │ ├── arbitrum-sepolia.svg
│ │ ├── arbitrum.svg
│ │ ├── base-grayscale.svg
│ │ ├── base-sepolia-grayscale.svg
│ │ ├── base-sepolia.svg
│ │ ├── base.svg
│ │ ├── blast-grayscale.svg
│ │ ├── blast-sepolia-grayscale.svg
│ │ ├── blast-sepolia.svg
│ │ ├── blast.svg
│ │ ├── bsc-grayscale.svg
│ │ ├── bsc.svg
│ │ ├── doctor-who-grayscale.svg
│ │ ├── doctor-who.svg
│ │ ├── ink-grayscale.svg
│ │ ├── ink.svg
│ │ ├── lens-grayscale.svg
│ │ ├── lens-sepolia-grayscale.svg
│ │ ├── lens-sepolia.svg
│ │ ├── lens.svg
│ │ ├── linea-grayscale.svg
│ │ ├── linea.svg
│ │ ├── lisk-grayscale.svg
│ │ ├── lisk-sepolia-grayscale.svg
│ │ ├── lisk-sepolia.svg
│ │ ├── lisk.svg
│ │ ├── mainnet-grayscale.svg
│ │ ├── mainnet.svg
│ │ ├── mode-grayscale.svg
│ │ ├── mode-sepolia-grayscale.svg
│ │ ├── mode-sepolia.svg
│ │ ├── mode.svg
│ │ ├── optimism-grayscale.svg
│ │ ├── optimism-sepolia-grayscale.svg
│ │ ├── optimism-sepolia.svg
│ │ ├── optimism.svg
│ │ ├── polygon-amoy-grayscale.svg
│ │ ├── polygon-amoy.svg
│ │ ├── polygon-grayscale.svg
│ │ ├── polygon.svg
│ │ ├── redstone-grayscale.svg
│ │ ├── redstone.svg
│ │ ├── scroll-grayscale.svg
│ │ ├── scroll.svg
│ │ ├── sepolia-grayscale.svg
│ │ ├── sepolia.svg
│ │ ├── soneium-grayscale.svg
│ │ ├── soneium.svg
│ │ ├── tatara-grayscale.svg
│ │ ├── tatara.svg
│ │ ├── unichain-grayscale.svg
│ │ ├── unichain-sepolia-grayscale.svg
│ │ ├── unichain-sepolia.svg
│ │ ├── unichain.svg
│ │ ├── world-chain-grayscale.svg
│ │ ├── world-chain.svg
│ │ ├── zk-sync-grayscale.svg
│ │ ├── zk-sync.svg
│ │ ├── zora-grayscale.svg
│ │ └── zora.svg
│ ├── extern-logos
│ │ ├── hyperliquid-grayscale.svg
│ │ └── hyperliquid.svg
│ ├── icons
│ │ ├── arrow-star-ring.svg
│ │ ├── arrow-up-down.svg
│ │ ├── arrow-up-right-boxed.svg
│ │ ├── arrow-up-right.svg
│ │ ├── check-star-ring-opaque-completed.svg
│ │ ├── check-star-ring-opaque-pending.svg
│ │ ├── check.svg
│ │ ├── checkmark-circle.svg
│ │ ├── chevron-down.svg
│ │ ├── chevron-right.svg
│ │ ├── clock.svg
│ │ ├── connectors.svg
│ │ ├── copy.svg
│ │ ├── cross.svg
│ │ ├── discord.svg
│ │ ├── edit.svg
│ │ ├── empty-clouds.svg
│ │ ├── farcaster.svg
│ │ ├── hamburger.svg
│ │ ├── hammer-star-ring.svg
│ │ ├── info.svg
│ │ ├── loading.svg
│ │ ├── plus-circle.svg
│ │ ├── powered-by-uma.svg
│ │ ├── question-circle.svg
│ │ ├── referral-within-star.svg
│ │ ├── referree.svg
│ │ ├── referrer.svg
│ │ ├── refresh.svg
│ │ ├── self-referral.svg
│ │ ├── settings.svg
│ │ ├── swap.svg
│ │ ├── user.svg
│ │ ├── wallet.svg
│ │ ├── x-blue-check.svg
│ │ ├── x-grey.svg
│ │ ├── x-star-ring.svg
│ │ ├── x-white.svg
│ │ └── zap.svg
│ └── token-logos
│ │ ├── acx.svg
│ │ ├── arb.svg
│ │ ├── bal.svg
│ │ ├── bnb.svg
│ │ ├── boba.svg
│ │ ├── cake.svg
│ │ ├── dai.svg
│ │ ├── eth.svg
│ │ ├── gho.svg
│ │ ├── lsk.svg
│ │ ├── matic.svg
│ │ ├── op.svg
│ │ ├── pool.svg
│ │ ├── snx.svg
│ │ ├── uma.svg
│ │ ├── uni.svg
│ │ ├── usdb.svg
│ │ ├── usdc.svg
│ │ ├── usdt.svg
│ │ ├── wbtc.svg
│ │ ├── weth.svg
│ │ └── wld.svg
├── components
│ ├── Alert
│ │ ├── Alert.styles.ts
│ │ ├── Alert.tsx
│ │ └── index.ts
│ ├── AmountInput
│ │ ├── AmountInput.tsx
│ │ ├── InputErrorText.tsx
│ │ └── index.tsx
│ ├── AmpliTrace
│ │ ├── AmpliTrace.tsx
│ │ └── index.tsx
│ ├── Badge
│ │ ├── Badge.tsx
│ │ └── index.tsx
│ ├── Banner
│ │ ├── Banner.styles.tsx
│ │ ├── Banner.tsx
│ │ └── index.ts
│ ├── BouncingDotsLoader
│ │ ├── BouncingDotsLoader.tsx
│ │ └── index.ts
│ ├── BreadcrumbV2
│ │ ├── BreadcrumbV2.tsx
│ │ ├── index.ts
│ │ └── useBreadcrumb.ts
│ ├── Button
│ │ ├── Button.tsx
│ │ └── index.ts
│ ├── CardWrapper
│ │ ├── CardWrapper.tsx
│ │ └── index.ts
│ ├── DepositsTable
│ │ ├── DataRow.tsx
│ │ ├── DepositsTable.tsx
│ │ ├── HeadRow.tsx
│ │ ├── PaginatedDepositsTable.tsx
│ │ ├── cells
│ │ │ ├── ActionsCell.tsx
│ │ │ ├── AddressCell.tsx
│ │ │ ├── AmountCell.tsx
│ │ │ ├── AssetCell.tsx
│ │ │ ├── BaseCell.tsx
│ │ │ ├── BridgeFeeCell.tsx
│ │ │ ├── DateCell.tsx
│ │ │ ├── NetFeeCell.tsx
│ │ │ ├── RateCell.tsx
│ │ │ ├── RewardsCell.tsx
│ │ │ ├── RouteCell.tsx
│ │ │ ├── StatusCell.tsx
│ │ │ └── TxCell.tsx
│ │ ├── hooks
│ │ │ └── useDepositStatus.ts
│ │ └── index.tsx
│ ├── ErrorBoundary
│ │ ├── ErrorBoundary.styles.tsx
│ │ ├── ErrorBoundary.tsx
│ │ └── index.tsx
│ ├── ExternalLink
│ │ ├── ExternalLink.tsx
│ │ └── index.ts
│ ├── Footer
│ │ ├── Footer.styles.tsx
│ │ ├── Footer.tsx
│ │ └── index.ts
│ ├── GlobalStyles
│ │ ├── GlobalStyles.tsx
│ │ ├── index.ts
│ │ └── reset.ts
│ ├── Header
│ │ ├── Header.styles.tsx
│ │ ├── Header.tsx
│ │ ├── MenuToggle.tsx
│ │ ├── __tests__
│ │ │ └── utils.test.ts
│ │ ├── index.ts
│ │ └── utils.ts
│ ├── IconPair
│ │ ├── IconPair.tsx
│ │ └── index.ts
│ ├── LayoutV2
│ │ ├── LayoutV2.tsx
│ │ └── index.ts
│ ├── Loader
│ │ ├── Loader.tsx
│ │ └── index.tsx
│ ├── LoadingSkeleton
│ │ ├── LoadingSkeleton.tsx
│ │ └── index.tsx
│ ├── Modal
│ │ ├── Modal.styles.ts
│ │ ├── Modal.tsx
│ │ └── index.ts
│ ├── Pagination
│ │ ├── Pagination.styles.tsx
│ │ ├── PaginationExample.tsx
│ │ ├── index.ts
│ │ ├── paginate.d.ts
│ │ └── paginate.ts
│ ├── ProgressBar
│ │ ├── ProgressBar.styles.tsx
│ │ ├── ProgressBar.tsx
│ │ └── index.ts
│ ├── RewardTable
│ │ ├── RewardTable.tsx
│ │ ├── RewardTables.styles.tsx
│ │ └── index.ts
│ ├── ScrollToTop
│ │ ├── ScrollToTop.tsx
│ │ └── index.ts
│ ├── SectionTitleWrapperV2
│ │ ├── SectionWrapperV2.tsx
│ │ └── index.ts
│ ├── Selector
│ │ ├── Selector.tsx
│ │ ├── index.ts
│ │ └── useSelector.ts
│ ├── Sidebar
│ │ ├── Sidebar.styles.tsx
│ │ ├── Sidebar.tsx
│ │ ├── index.ts
│ │ └── useSidebar.ts
│ ├── SuperHeader
│ │ ├── SuperHeader.tsx
│ │ └── index.ts
│ ├── Table
│ │ ├── Table.d.ts
│ │ ├── Table.styles.tsx
│ │ ├── TableExample.tsx
│ │ └── index.ts
│ ├── Tabs
│ │ └── index.tsx
│ ├── Text
│ │ ├── Text.tsx
│ │ └── index.ts
│ ├── Toast
│ │ ├── Toast.styles.tsx
│ │ ├── Toast.tsx
│ │ ├── index.ts
│ │ ├── toast.d.ts
│ │ └── useToast.tsx
│ ├── Tooltip
│ │ ├── Tooltip.styles.tsx
│ │ ├── Tooltip.tsx
│ │ └── index.ts
│ ├── Wallet
│ │ ├── Wallet.styles.ts
│ │ ├── Wallet.tsx
│ │ ├── Web3Subscribe.tsx
│ │ └── index.ts
│ └── index.ts
├── constants
│ ├── chains
│ │ ├── configs.ts
│ │ ├── index.ts
│ │ └── utils.ts
│ ├── pools.ts
│ └── tokens.ts
├── custom.d.ts
├── data
│ ├── chains_1.json
│ ├── chains_11155111.json
│ ├── examples
│ │ ├── dynamic-weights.json
│ │ ├── exclusive-relayers.json
│ │ ├── exclusivity-fill-times.json
│ │ ├── exclusivity-strategy.json
│ │ ├── fill-times.json
│ │ ├── fixed-weights.json
│ │ └── rpc-providers.json
│ ├── routes_11155111_0x14224e63716afAcE30C9a417E0542281869f7d9e.json
│ ├── routes_1_0xc186fA914353c44b2E33eBE05f21846F1048bEda.json
│ └── universal-swap-routes_1.json
├── hooks
│ ├── index.ts
│ ├── tests
│ │ ├── usePrevious.test.ts
│ │ ├── useScrollPosition.test.ts
│ │ └── useWindowSize.test.ts
│ ├── useAmpliTracking.ts
│ ├── useAmplitude.tsx
│ ├── useApprove.ts
│ ├── useAvailableRemainingRewards.ts
│ ├── useBalance.ts
│ ├── useBridgeFees.ts
│ ├── useBridgeLimits.ts
│ ├── useCenteredInViewport.ts
│ ├── useClickOutsideModal.ts
│ ├── useCoingeckoPrice.ts
│ ├── useConnection.ts
│ ├── useCurrentBreakpoint.ts
│ ├── useDeposits.ts
│ ├── useDisallowList.ts
│ ├── useDiscord.ts
│ ├── useElapsedSeconds.ts
│ ├── useEns.ts
│ ├── useError.tsx
│ ├── useIndexerDepositTracking.ts
│ ├── useInitialUserPropTraces.ts
│ ├── useIsContractAddress.ts
│ ├── useIsWrongNetwork.ts
│ ├── useLoadAmpli.ts
│ ├── useOnboard.tsx
│ ├── usePageScrollLock.ts
│ ├── usePrevious.ts
│ ├── useQueryParams.ts
│ ├── useQueue.ts
│ ├── useReferralLink.ts
│ ├── useReferrer.ts
│ ├── useRewardSummary.ts
│ ├── useRewards.ts
│ ├── useRouteTrace.ts
│ ├── useScrollElementByHashIntoView.ts
│ ├── useScrollPosition.ts
│ ├── useStakingPool.ts
│ ├── useSwapQuote.ts
│ ├── useTokenConversion.ts
│ ├── useUnclaimedProofs.ts
│ ├── useUniversalSwapQuote.ts
│ ├── useWalletBalanceTrace.ts
│ ├── useWalletTokenImport.ts
│ ├── useWalletTrace.ts
│ └── useWindowSize.ts
├── index.tsx
├── mp4.d.ts
├── onboard-override.css
├── stories
│ ├── Alert.stories.tsx
│ ├── AmountInput.stories.tsx
│ ├── Badge.stories.tsx
│ ├── BouncingDotsLoader.stories.tsx
│ ├── DepositsTable.stories.tsx
│ ├── Footer.stories.tsx
│ ├── Header.stories.tsx
│ ├── Modal.stories.tsx
│ ├── PaginatedDepositsTable.stories.tsx
│ ├── assets
│ │ ├── code-brackets.svg
│ │ ├── colors.svg
│ │ ├── comments.svg
│ │ ├── direction.svg
│ │ ├── flow.svg
│ │ ├── plugin.svg
│ │ ├── repo.svg
│ │ └── stackalt.svg
│ └── buttons
│ │ ├── PrimaryButton.stories.tsx
│ │ └── SecondaryButton.stories.tsx
├── utils
│ ├── address.ts
│ ├── amplitude.ts
│ ├── bignumber.ts
│ ├── bridge.ts
│ ├── config.ts
│ ├── constants.ts
│ ├── convertdecimals.ts
│ ├── deposits.ts
│ ├── errors.ts
│ ├── ethers.ts
│ ├── format.ts
│ ├── hyperliquid.ts
│ ├── index.ts
│ ├── lazy-with-retry.ts
│ ├── local-deposits.ts
│ ├── localStorage.ts
│ ├── math.ts
│ ├── merkle-distributor.ts
│ ├── network.ts
│ ├── notify.ts
│ ├── onboard.ts
│ ├── pool.ts
│ ├── providers.ts
│ ├── query-keys.ts
│ ├── rewards.ts
│ ├── sdk.ts
│ ├── sentry.ts
│ ├── serverless-api
│ │ ├── index.ts
│ │ ├── mock-adapter.ts
│ │ ├── mocked
│ │ │ ├── bridge-limits.mocked.ts
│ │ │ ├── coingecko.mocked.ts
│ │ │ ├── connect-linked-wallet.mocked.ts
│ │ │ ├── get-deposit-stats.mocked.ts
│ │ │ ├── index.ts
│ │ │ ├── pools-user.mocked.ts
│ │ │ ├── pools.mocked.ts
│ │ │ ├── retrieve-linked-wallet.mocked.ts
│ │ │ ├── retrieve-user-details.mocked.ts
│ │ │ ├── rewards.mocked.ts
│ │ │ ├── suggested-fees.mocked.ts
│ │ │ ├── swap-approval.ts
│ │ │ └── swap-quote.ts
│ │ ├── prod
│ │ │ ├── coingecko.ts
│ │ │ ├── connect-linked-wallet.prod.ts
│ │ │ ├── get-deposit-stats.prod.ts
│ │ │ ├── index.ts
│ │ │ ├── pools-user.ts
│ │ │ ├── pools.ts
│ │ │ ├── retrieve-discord-user-details.prod.ts
│ │ │ ├── retrieve-linked-wallet.prod.ts
│ │ │ ├── retrieveLimits.ts
│ │ │ ├── rewards.ts
│ │ │ ├── suggested-fees.prod.ts
│ │ │ ├── swap-approval.ts
│ │ │ └── swap-quote.ts
│ │ └── types.ts
│ ├── staking-pool.ts
│ ├── ternary.ts
│ ├── tests
│ │ ├── config.test.ts
│ │ ├── format.test.ts
│ │ ├── rewards.test.ts
│ │ └── weiMath.test.ts
│ ├── time.ts
│ ├── token.ts
│ ├── transactions.ts
│ ├── typechain.ts
│ ├── types.ts
│ ├── url.ts
│ ├── wait.ts
│ └── weiMath.ts
├── views
│ ├── Bridge
│ │ ├── Bridge.styles.tsx
│ │ ├── Bridge.tsx
│ │ ├── components
│ │ │ ├── AmountInput.tsx
│ │ │ ├── Breadcrumb.tsx
│ │ │ ├── BridgeForm.tsx
│ │ │ ├── ChainSelector.tsx
│ │ │ ├── ChangeAccountModal.tsx
│ │ │ ├── EstimatedTable.tsx
│ │ │ ├── FeesCollapsible.tsx
│ │ │ ├── QuickSwap.tsx
│ │ │ ├── RecipientRow.tsx
│ │ │ ├── RewardsProgramCTA.tsx
│ │ │ ├── RouteNotSupportedTooltipText.tsx
│ │ │ ├── SwapSlippageModal.tsx
│ │ │ ├── TokenFee.tsx
│ │ │ └── TokenSelector.tsx
│ │ ├── hooks
│ │ │ ├── useAmountInput.ts
│ │ │ ├── useBridge.ts
│ │ │ ├── useBridgeAction.ts
│ │ │ ├── useEstimatedRewards.ts
│ │ │ ├── useMaxBalance.ts
│ │ │ ├── useSelectRoute.ts
│ │ │ ├── useToAccount.ts
│ │ │ └── useTransferQuote.ts
│ │ ├── index.ts
│ │ └── utils.ts
│ ├── DepositStatus
│ │ ├── DepositStatus.tsx
│ │ ├── components
│ │ │ ├── Breadcrumb.tsx
│ │ │ ├── BuildOnAcrossCard.tsx
│ │ │ ├── DepositStatusAnimatedIcons.tsx
│ │ │ ├── DepositStatusLowerCard.tsx
│ │ │ ├── DepositStatusUpperCard.tsx
│ │ │ ├── DepositTimesCard.tsx
│ │ │ ├── EarnActionCard.tsx
│ │ │ ├── EarnByLpAndStakingCard.tsx
│ │ │ ├── ElapsedTime.tsx
│ │ │ ├── SharedSocialsCard.tsx
│ │ │ └── SocialShareButton.tsx
│ │ ├── hooks
│ │ │ ├── useDepositTracking.ts
│ │ │ └── useResolveFromBridgePagePayload.ts
│ │ ├── index.tsx
│ │ ├── types.ts
│ │ └── utils.ts
│ ├── LiquidityPool
│ │ ├── LiquidityPool.styles.tsx
│ │ ├── LiquidityPool.tsx
│ │ ├── components
│ │ │ ├── ActionInputBlock.tsx
│ │ │ ├── Breadcrumb.tsx
│ │ │ ├── EarnByStakingInfoBox.tsx
│ │ │ ├── PoolSelector.tsx
│ │ │ ├── StatBox.tsx
│ │ │ └── UserStatRow.tsx
│ │ ├── hooks
│ │ │ ├── useLiquidityAction.ts
│ │ │ ├── useLiquidityPool.ts
│ │ │ ├── useMaxAmounts.ts
│ │ │ ├── useUserLiquidityPool.ts
│ │ │ └── useValidAmount.ts
│ │ ├── index.ts
│ │ └── utils.ts
│ ├── NotFound
│ │ ├── NotFound.styles.tsx
│ │ ├── NotFound.tsx
│ │ └── index.ts
│ ├── Rewards
│ │ ├── Rewards.style.tsx
│ │ ├── Rewards.tsx
│ │ ├── components
│ │ │ ├── AdditionalQuestionCTA.tsx
│ │ │ ├── ChainLogoOverlap.tsx
│ │ │ ├── GenericOverviewCard.tsx
│ │ │ ├── GenericStakingPoolTable
│ │ │ │ ├── GenericStakingPoolFormatter.tsx
│ │ │ │ ├── GenericStakingPoolTable.styles.tsx
│ │ │ │ ├── GenericStakingPoolTable.tsx
│ │ │ │ └── index.ts
│ │ │ ├── OverviewSection.tsx
│ │ │ ├── RewardProgramCard.tsx
│ │ │ └── RewardProgramSection.tsx
│ │ ├── hooks
│ │ │ ├── useRewardProgramCard.ts
│ │ │ ├── useRewards.ts
│ │ │ └── useStakingPools.tsx
│ │ └── index.ts
│ ├── RewardsProgram
│ │ ├── ARBRebatesProgram.tsx
│ │ ├── GenericRewardsProgram
│ │ │ ├── ClaimRewardsModal.tsx
│ │ │ ├── GenericCard.tsx
│ │ │ ├── GenericEmptyTable.tsx
│ │ │ ├── GenericInformationCard.tsx
│ │ │ ├── GenericRewardClaimCard.tsx
│ │ │ └── GenericRewardsProgram.tsx
│ │ ├── OPRebatesProgram.tsx
│ │ └── hooks
│ │ │ ├── useARBRebatesProgram.ts
│ │ │ ├── useClaimModal.ts
│ │ │ ├── useClaimReferralRewards.ts
│ │ │ ├── useGenericRewardClaimCard.ts
│ │ │ ├── useGenericRewardProgram.ts
│ │ │ └── useOPRebatesProgram.ts
│ ├── Staking
│ │ ├── Staking.styles.tsx
│ │ ├── Staking.tsx
│ │ ├── components
│ │ │ ├── ConnectWalletButton.tsx
│ │ │ ├── StakingExitAction
│ │ │ │ ├── StakingExitAction.styles.tsx
│ │ │ │ ├── StakingExitAction.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── StakingForm
│ │ │ │ ├── StakingForm.styles.tsx
│ │ │ │ ├── StakingForm.tsx
│ │ │ │ └── index.ts
│ │ │ ├── StakingInputBlock
│ │ │ │ ├── StakingInputBlock.styles.tsx
│ │ │ │ ├── StakingInputBlock.tsx
│ │ │ │ └── index.ts
│ │ │ ├── StakingReward
│ │ │ │ ├── StakingReward.tsx
│ │ │ │ └── index.ts
│ │ │ └── index.ts
│ │ ├── hooks
│ │ │ ├── useClaimStakeRewardAction.ts
│ │ │ ├── useStakeFormLogic.ts
│ │ │ ├── useStakingAction.ts
│ │ │ └── useStakingView.ts
│ │ ├── index.ts
│ │ └── types.ts
│ ├── Transactions
│ │ ├── Transactions.tsx
│ │ ├── components
│ │ │ ├── EmptyTable.tsx
│ │ │ ├── FilterDropdown.tsx
│ │ │ ├── PersonalTransactions.tsx
│ │ │ └── SpeedUpModal
│ │ │ │ ├── InputWithButton.tsx
│ │ │ │ ├── SpeedUpModal.styles.tsx
│ │ │ │ ├── SpeedUpModal.tsx
│ │ │ │ ├── SpeedUpStats.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ └── utils.tsx
│ │ ├── hooks
│ │ │ ├── usePagination.tsx
│ │ │ ├── usePersonalTransactions.tsx
│ │ │ └── useSpeedUp.tsx
│ │ ├── index.ts
│ │ └── types.ts
│ └── index.ts
├── vite-env.d.ts
└── wagmi.config.ts
├── test
└── api
│ ├── _constants.test.ts
│ ├── _dexes
│ └── utils.test.ts
│ ├── _utils.test.ts
│ └── main.test.ts
├── tsconfig.e2e.json
├── tsconfig.json
├── vercel.json
├── vite.config.js
└── yarn.lock
/.env.test:
--------------------------------------------------------------------------------
1 | REACT_APP_V_ETH=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
2 | REACT_APP_UMA_ADDRESS=0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828
--------------------------------------------------------------------------------
/.github/actions/setup/action.yaml:
--------------------------------------------------------------------------------
1 | runs:
2 | using: composite
3 | steps:
4 | - uses: actions/checkout@v4
5 |
6 | - uses: actions/setup-node@v4
7 | with:
8 | node-version: 20
9 | registry-url: https://registry.npmjs.org
10 | cache: yarn
11 |
12 | - run: yarn install --frozen-lockfile --ignore-scripts
13 | shell: bash
14 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "daily"
7 | groups:
8 | actions-deps:
9 | patterns:
10 | - "*"
11 | - package-ecosystem: "npm"
12 | directory: "/"
13 | schedule:
14 | interval: "daily"
15 | groups:
16 | dev-deps:
17 | dependency-type: "development"
18 | prod-deps:
19 | dependency-type: "production"
20 | ignore:
21 | - dependency-name: "*"
22 | update-types: ["version-update:semver-major"]
23 | # Packages that need manual upgrades or should be pinned to a specific version
24 | - dependency-name: "@across-protocol/sdk"
25 | - dependency-name: "@across-protocol/contracts"
26 | - dependency-name: "@across-protocol/constants"
27 | - dependency-name: "@balancer-labs/sdk"
28 |
--------------------------------------------------------------------------------
/.github/workflows/dependency-review.yaml:
--------------------------------------------------------------------------------
1 | name: "Dependency Review"
2 | on: [pull_request]
3 |
4 | permissions:
5 | contents: read
6 |
7 | jobs:
8 | dependency-review:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 | - name: "Checkout Repository"
13 | uses: actions/checkout@v4
14 | - name: "Dependency Review"
15 | uses: actions/dependency-review-action@v4
16 | with:
17 | fail-on-severity: high
18 | # Comma-separated list of GHSA IDs to allow.
19 | # We allow the following critical vulnerabilities
20 | # because they are not exploitable in our usage of the package.
21 | # - @openzeppelin/contracts@3.4.1-solc-0.7-2
22 | # - @openzeppelin/contracts@4.7.0
23 | allow-ghsas: GHSA-fg47-3c2x-m2wr, GHSA-88g8-f5mf-f5rj, GHSA-3h5v-q93c-6h6q, GHSA-qh9x-gcfh-pcrw, GHSA-4g63-c64m-25w9, GHSA-xrc4-737v-9q75, GHSA-4h98-2769-gh6h, GHSA-4h98-2769-gh6h, GHSA-93hq-5wgc-jc82
24 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | yarn lint-staged
5 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | lts/iron
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5"
3 | }
4 |
--------------------------------------------------------------------------------
/.storybook/preview.tsx:
--------------------------------------------------------------------------------
1 | import { Preview } from "@storybook/react";
2 | import { MemoryRouter } from "react-router-dom";
3 |
4 | import { default as GlobalStyles } from "../src/components/GlobalStyles/GlobalStyles";
5 | import { OnboardContext, useOnboardManager } from "../src/hooks/useOnboard";
6 |
7 | const preview: Preview = {
8 | decorators: [
9 | (Story) => (
10 |
11 |
12 |
13 |
14 |
15 |
16 | ),
17 | ],
18 | parameters: {
19 | actions: { argTypesRegex: "^on[A-Z].*" },
20 | controls: {
21 | matchers: {
22 | color: /(background|color)$/i,
23 | date: /Date$/,
24 | },
25 | },
26 | },
27 | };
28 |
29 | export default preview;
30 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 |
2 | # This is a comment.
3 | # Each line is a file pattern followed by one or more owners.
4 | # Note: CODEOWNERS are automatically requested for review on relevant PRs.
5 | # Order is important; the last matching pattern takes the most
6 | # precedence.
7 |
8 | # These owners will be the default owners for everything in
9 | # the repo unless a later match takes precedence.
10 | * @mrice32 @nicholaspai @dohaki @james-a-morris @gsteenkamp89
11 |
12 | # Serverless api
13 | /api/ @mrice32 @nicholaspai @dohaki @james-a-morris @pxrl
14 |
15 |
--------------------------------------------------------------------------------
/api/_address.ts:
--------------------------------------------------------------------------------
1 | import { isAddress as isEvmAddress } from "viem";
2 | import { isAddress as isSvmAddress } from "@solana/kit";
3 |
4 | // exports
5 | export { isSvmAddress, isEvmAddress };
6 |
--------------------------------------------------------------------------------
/api/_auth.ts:
--------------------------------------------------------------------------------
1 | import { TypedVercelRequest } from "./_types";
2 | import { getEnvs } from "./_env";
3 |
4 | const { VERCEL_AUTOMATION_BYPASS_SECRET } = getEnvs();
5 |
6 | export const Role = {
7 | // Requests with this role will be allowed to access opt-in chains
8 | OPT_IN_CHAINS: "opt-in-chains",
9 | };
10 |
11 | export function parseRole(req: TypedVercelRequest) {
12 | const xVercelProtectionBypass =
13 | req.headers?.["x-vercel-protection-bypass"] ||
14 | req.query?.["x-vercel-protection-bypass"];
15 | if (
16 | xVercelProtectionBypass &&
17 | xVercelProtectionBypass === VERCEL_AUTOMATION_BYPASS_SECRET
18 | ) {
19 | return "opt-in-chains";
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/api/_base/api-adapter.ts:
--------------------------------------------------------------------------------
1 | import { ApiHandler } from "./api-handler";
2 |
3 | export abstract class ApiAdapter {
4 | protected abstract adapt(handler: ApiHandler): THandler;
5 |
6 | public adaptHandler(handler: ApiHandler): THandler {
7 | return this.adapt(handler);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/api/_dexes/uniswap/utils/conversion.ts:
--------------------------------------------------------------------------------
1 | import { Percent } from "@uniswap/sdk-core";
2 |
3 | export function floatToPercent(value: number) {
4 | return new Percent(
5 | // max. slippage decimals is 2
6 | Number(value.toFixed(2)) * 100,
7 | 10_000
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/api/_eip712.ts:
--------------------------------------------------------------------------------
1 | import { utils } from "ethers";
2 |
3 | export function hashDomainSeparator(params: {
4 | name: string;
5 | version: string | number;
6 | chainId: number;
7 | verifyingContract: string;
8 | }): string {
9 | return utils.keccak256(
10 | utils.defaultAbiCoder.encode(
11 | ["bytes32", "bytes32", "bytes32", "uint256", "address"],
12 | [
13 | utils.id(
14 | "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
15 | ),
16 | utils.id(params.name),
17 | utils.id(params.version.toString()),
18 | params.chainId,
19 | params.verifyingContract,
20 | ]
21 | )
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/api/_env.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import dotenv from "dotenv";
3 |
4 | dotenv.config({
5 | path: [".env.local", ".env"].map((file) => path.join(process.cwd(), file)),
6 | });
7 |
8 | export const getEnvs = () => {
9 | return process.env;
10 | };
11 |
--------------------------------------------------------------------------------
/api/_exclusivity/strategies/index.ts:
--------------------------------------------------------------------------------
1 | import * as sdk from "@across-protocol/sdk";
2 | const { ZERO_ADDRESS } = sdk.constants;
3 |
4 | export * from "./weighted-random";
5 |
6 | // Default strategy
7 | export const none = (_: string[]) => ZERO_ADDRESS;
8 |
--------------------------------------------------------------------------------
/api/_exclusivity/types.ts:
--------------------------------------------------------------------------------
1 | export type ExclusiveRelayer = {
2 | exclusiveRelayer: string;
3 | exclusivityPeriod: number; // Absolute number of seconds of exclusivity from deposit time.
4 | };
5 |
6 | // Initial relayer configuration items.
7 | export type RelayerConfig = {
8 | address: string;
9 | minExclusivityPeriod: number;
10 | minProfitThreshold: number;
11 | balanceMultiplier: number;
12 | maxFillSize: number;
13 | originChainIds: number[];
14 | };
15 |
16 | export type CandidateRelayer = {
17 | address: string;
18 | dynamicWeight: number;
19 | fixedWeight: number;
20 | };
21 |
22 | export type RelayerSelector = (relayers: string[]) => string;
23 |
--------------------------------------------------------------------------------
/api/_fill-deadline.ts:
--------------------------------------------------------------------------------
1 | import { DEFAULT_FILL_DEADLINE_BUFFER_SECONDS } from "./_constants";
2 | import { getSpokePool } from "./_utils";
3 |
4 | function getFillDeadlineBuffer(chainId: number) {
5 | const bufferFromEnv = (
6 | JSON.parse(process.env.FILL_DEADLINE_BUFFER_SECONDS || "{}") as Record<
7 | string,
8 | string
9 | >
10 | )?.[chainId.toString()];
11 | return Number(bufferFromEnv ?? DEFAULT_FILL_DEADLINE_BUFFER_SECONDS);
12 | }
13 |
14 | export async function getFillDeadline(chainId: number): Promise {
15 | const fillDeadlineBuffer = getFillDeadlineBuffer(chainId);
16 | const spokePool = getSpokePool(chainId);
17 | const currentTime = await spokePool.callStatic.getCurrentTime();
18 | return Number(currentTime) + fillDeadlineBuffer;
19 | }
20 |
--------------------------------------------------------------------------------
/api/_integrator-id.ts:
--------------------------------------------------------------------------------
1 | import { utils } from "ethers";
2 |
3 | export const DOMAIN_CALLDATA_DELIMITER = "0x1dc0de";
4 |
5 | export function isValidIntegratorId(integratorId: string) {
6 | return (
7 | utils.isHexString(integratorId) &&
8 | // "0x" + 2 bytes = 6 hex characters
9 | integratorId.length === 6
10 | );
11 | }
12 |
13 | export function assertValidIntegratorId(integratorId: string) {
14 | if (!isValidIntegratorId(integratorId)) {
15 | throw new Error(
16 | `Invalid integrator ID: ${integratorId}. Needs to be 2 bytes hex string.`
17 | );
18 | }
19 |
20 | return true;
21 | }
22 |
23 | export function tagIntegratorId(integratorId: string, txData: string) {
24 | assertValidIntegratorId(integratorId);
25 |
26 | return utils.hexlify(
27 | utils.concat([txData, DOMAIN_CALLDATA_DELIMITER, integratorId])
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/api/_typeguards.ts:
--------------------------------------------------------------------------------
1 | export const isError = (input: unknown): input is Error =>
2 | input instanceof Error;
3 |
4 | export const isPromiseRejectedResult = (
5 | input: PromiseSettledResult
6 | ): input is PromiseRejectedResult => input.status === "rejected";
7 |
--------------------------------------------------------------------------------
/api/_types/generic.types.ts:
--------------------------------------------------------------------------------
1 | import { VercelRequest } from "@vercel/node";
2 |
3 | export type TypedVercelRequest = VercelRequest & {
4 | query: Partial;
5 | };
6 |
--------------------------------------------------------------------------------
/api/_types/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./generic.types";
2 | export * from "./utility.types";
3 |
--------------------------------------------------------------------------------
/api/_types/utility.types.ts:
--------------------------------------------------------------------------------
1 | export type PoolStateResult = {
2 | estimatedApy: string;
3 | exchangeRateCurrent: string;
4 | totalPoolSize: string;
5 | liquidityUtilizationCurrent: string;
6 | };
7 |
8 | export type PoolStateOfUser = {
9 | address: string;
10 | poolAddress: string;
11 | lpTokens: string;
12 | positionValue: string;
13 | totalDeposited: string;
14 | feesEarned: string;
15 | };
16 |
17 | export type TokenInfo = {
18 | symbol: string;
19 | address: string;
20 | decimals: number;
21 | name: string;
22 | addresses: Record;
23 | };
24 |
25 | export type DepositRoute = {
26 | originChainId: number;
27 | originToken: string;
28 | destinationChainId: number;
29 | destinationToken: string;
30 | originTokenSymbol: string;
31 | destinationTokenSymbol: string;
32 | };
33 |
--------------------------------------------------------------------------------
/e2e/synpress.ts:
--------------------------------------------------------------------------------
1 | import { metaMaskFixtures, testWithSynpress } from "@synthetixio/synpress";
2 | import connectedSetup from "./wallet-setup/connected.setup";
3 |
4 | export default testWithSynpress(metaMaskFixtures(connectedSetup));
5 |
--------------------------------------------------------------------------------
/e2e/tests/04_transactions_page.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 | import { metaMaskFixtures, testWithSynpress } from "@synthetixio/synpress";
3 |
4 | import { E2E_DAPP_URL } from "../config";
5 | import connectedSetup from "../wallet-setup/connected.setup";
6 |
7 | const testWithConnectedMM = testWithSynpress(metaMaskFixtures(connectedSetup));
8 |
9 | const txPageUrl = E2E_DAPP_URL + "/transactions";
10 |
11 | test("renders correctly /transactions - disconnected", async ({ page }) => {
12 | await page.goto(txPageUrl);
13 |
14 | await expect(
15 | page.getByText(/Please connect your wallet to view transactions/)
16 | ).toBeVisible();
17 | });
18 |
19 | testWithConnectedMM(
20 | "renders correctly /rewards - connected",
21 | async ({ page, metamask }) => {
22 | await metamask.switchNetwork("Ethereum Mainnet");
23 |
24 | await page.goto(txPageUrl);
25 |
26 | await expect(
27 | page.getByText(/Please connect your wallet to view transactions/)
28 | ).not.toBeVisible();
29 | }
30 | );
31 |
--------------------------------------------------------------------------------
/e2e/wallet-setup/basic.setup.ts:
--------------------------------------------------------------------------------
1 | import { MetaMask, defineWalletSetup } from "@synthetixio/synpress";
2 |
3 | const SEED_PHRASE =
4 | "test test test test test test test test test test test junk";
5 | const PASSWORD = "SynpressIsAwesomeNow!!!";
6 |
7 | export default defineWalletSetup(PASSWORD, async (context, walletPage) => {
8 | const metamask = new MetaMask(context, walletPage, PASSWORD);
9 |
10 | await metamask.importWallet(SEED_PHRASE);
11 | });
12 |
--------------------------------------------------------------------------------
/funding.json:
--------------------------------------------------------------------------------
1 | {
2 | "opRetro": {
3 | "projectId": "0x72723e07fe409557489a6643b43d9493a94c10ba68230b0527f01834cb6a550f"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/jest.config.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */
2 |
3 | module.exports = {
4 | setupFiles: ["/setup.jest.ts"],
5 | preset: "ts-jest",
6 | testEnvironment: "node",
7 | moduleDirectories: ["node_modules", ""],
8 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
9 | transform: {
10 | "^.+\\.tsx?$": "ts-jest",
11 | "^.+\\.svg$": "jest-transform-stub",
12 | "^.+\\.png$": "jest-transform-stub",
13 | },
14 | moduleNameMapper: {
15 | "^components/(.*)$": "/src/components/$1",
16 | "^utils/(.*)$": "/src/utils/$1",
17 | "^hooks/(.*)$": "/src/hooks/$1",
18 | "^assets/(.*)$": "/src/assets/$1",
19 | "^data/(.*)$": "/src/data/$1",
20 | uuid: require.resolve("uuid"),
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [[headers]]
2 | for = "/*"
3 | [headers.values]
4 | X-Content-Type-Options = "nosniff"
5 | X-Frame-Options = "DENY"
6 | X-XSS-Protection = "1; mode=block"
7 | Referrer-Policy = "strict-origin"
8 | Permissions-Policy = '''
9 | geolocation=(self),
10 | microphone=()'''
11 |
--------------------------------------------------------------------------------
/playwright.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig, devices } from "@playwright/test";
2 |
3 | import { E2E_DAPP_URL } from "./e2e/config";
4 |
5 | /**
6 | * See https://playwright.dev/docs/test-configuration.
7 | */
8 | export default defineConfig({
9 | timeout: 120_000,
10 | testDir: "./e2e",
11 | fullyParallel: true,
12 | forbidOnly: !!process.env.CI,
13 | retries: process.env.CI ? 2 : 0,
14 | workers: process.env.CI ? 1 : undefined,
15 | reporter: "html",
16 | use: {
17 | baseURL: E2E_DAPP_URL,
18 | trace: "on-first-retry",
19 | testIdAttribute: "data-cy",
20 | },
21 | projects: [
22 | {
23 | name: "chromium",
24 | use: { ...devices["Desktop Chrome"] },
25 | },
26 | ],
27 | });
28 |
--------------------------------------------------------------------------------
/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/public/fonts/Barlow-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/across-protocol/frontend/234e2fb182d6c5f4e1c7c1ead736f5edfa9c9a66/public/fonts/Barlow-Bold.woff2
--------------------------------------------------------------------------------
/public/fonts/Barlow-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/across-protocol/frontend/234e2fb182d6c5f4e1c7c1ead736f5edfa9c9a66/public/fonts/Barlow-Medium.woff2
--------------------------------------------------------------------------------
/public/fonts/Barlow-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/across-protocol/frontend/234e2fb182d6c5f4e1c7c1ead736f5edfa9c9a66/public/fonts/Barlow-Regular.woff2
--------------------------------------------------------------------------------
/public/logo-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/across-protocol/frontend/234e2fb182d6c5f4e1c7c1ead736f5edfa9c9a66/public/logo-small.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/sitemap.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | https://app.across.to/
4 | https://app.across.to/bridge
5 | https://app.across.to/pool
6 | https://app.across.to/rewards
7 |
--------------------------------------------------------------------------------
/public/thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/across-protocol/frontend/234e2fb182d6c5f4e1c7c1ead736f5edfa9c9a66/public/thumbnail.png
--------------------------------------------------------------------------------
/scripts/chain-configs/aleph-zero/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.ALEPH_ZERO;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | fullName: "Aleph Zero",
13 | logoPath: "./assets/logo.svg",
14 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
15 | spokePool: {
16 | address: getDeployedAddress("SpokePool", chainId),
17 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
18 | },
19 | chainId,
20 | publicRpcUrl: "https://rpc.alephzero.raas.gelato.cloud",
21 | blockTimeSeconds: 6,
22 | tokens: ["USDT", "WETH"],
23 | enableCCTP: false,
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/arbitrum-sepolia/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.ARBITRUM_SEPOLIA;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "../arbitrum/assets/logo.svg",
13 | grayscaleLogoPath: "../arbitrum/assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
20 | tokens: ["WETH", "ETH", "USDC"],
21 | enableCCTP: false,
22 | swapTokens: [],
23 | } as ChainConfig;
24 |
--------------------------------------------------------------------------------
/scripts/chain-configs/arbitrum/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
4 |
5 | import { ChainConfig } from "../types";
6 |
7 | const chainId = CHAIN_IDs.ARBITRUM;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | name: "Arbitrum",
13 | fullName: "Arbitrum One",
14 | logoPath: "./assets/logo.svg",
15 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
16 | spokePool: {
17 | address: getDeployedAddress("SpokePool", chainId),
18 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
19 | },
20 | publicRpcUrl: "https://arb1.arbitrum.io/rpc",
21 | chainId,
22 | tokens: ["WBTC", "USDC", "WETH", "ETH", "UMA", "DAI", "BAL", "ACX", "POOL"],
23 | enableCCTP: true,
24 | blockTimeSeconds: 1,
25 | } as ChainConfig;
26 |
--------------------------------------------------------------------------------
/scripts/chain-configs/base/assets/grayscale-logo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/scripts/chain-configs/base/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/scripts/chain-configs/base/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
4 |
5 | import { ChainConfig } from "../types";
6 |
7 | const chainId = CHAIN_IDs.BASE;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://mainnet.base.org",
20 | tokens: ["USDC", "USDT", "WETH", "ETH", "DAI", "BAL", "POOL"],
21 | enableCCTP: true,
22 | blockTimeSeconds: 2,
23 | disabledRoutes: [],
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/blast-sepolia/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
4 |
5 | import { ChainConfig } from "../types";
6 |
7 | const chainId = CHAIN_IDs.BLAST_SEPOLIA;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "../blast/assets/logo.svg",
13 | grayscaleLogoPath: "../blast/assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://sepolia.blast.io",
20 | blockTimeSeconds: 2,
21 | tokens: ["WETH", "ETH"],
22 | enableCCTP: false,
23 | swapTokens: [],
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/blast/assets/grayscale-logo.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/scripts/chain-configs/blast/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/scripts/chain-configs/blast/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
4 |
5 | import { ChainConfig } from "../types";
6 |
7 | const chainId = CHAIN_IDs.BLAST;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://rpc.blast.io",
20 | blockTimeSeconds: 2,
21 | tokens: ["WETH", "ETH", "USDB", "WBTC"],
22 | enableCCTP: false,
23 | swapTokens: [],
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/bsc/assets/grayscale-logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/scripts/chain-configs/bsc/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/scripts/chain-configs/bsc/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.BSC;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | fullName: "BNB Smart Chain",
13 | logoPath: "./assets/logo.svg",
14 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
15 | spokePool: {
16 | address: getDeployedAddress("SpokePool", chainId),
17 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
18 | },
19 | chainId,
20 | publicRpcUrl: chainInfoBase.publicRPC,
21 | blockTimeSeconds: 3,
22 | tokens: ["USDC-BNB", "USDT-BNB", "WBNB", "WETH"],
23 | enableCCTP: false,
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/ink/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.INK;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://rpc-gel.inkonchain.com",
20 | blockTimeSeconds: 1,
21 | tokens: ["WETH", "ETH"],
22 | enableCCTP: false,
23 | } as ChainConfig;
24 |
--------------------------------------------------------------------------------
/scripts/chain-configs/lens-sepolia/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.LENS_SEPOLIA;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://rpc.testnet.lens.dev",
20 | blockTimeSeconds: 1,
21 | tokens: ["GRASS", "WETH", "WGRASS"],
22 | enableCCTP: false,
23 | } as ChainConfig;
24 |
--------------------------------------------------------------------------------
/scripts/chain-configs/lens/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.LENS;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | nativeToken: "GHO",
13 | logoPath: "./assets/logo.svg",
14 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
15 | spokePool: {
16 | address: getDeployedAddress("SpokePool", chainId),
17 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
18 | },
19 | chainId,
20 | publicRpcUrl: chainInfoBase.publicRPC,
21 | blockTimeSeconds: 1,
22 | tokens: ["WGHO", "GHO", "WETH", "USDC"],
23 | enableCCTP: false,
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/linea/assets/grayscale-logo.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/scripts/chain-configs/linea/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/scripts/chain-configs/linea/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
4 |
5 | import { ChainConfig } from "../types";
6 |
7 | const chainId = CHAIN_IDs.LINEA;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | blockTimeSeconds: 2,
20 | publicRpcUrl: "https://rpc.linea.build",
21 | tokens: ["WETH", "ETH", "USDC", "USDT", "DAI", "WBTC"],
22 | enableCCTP: false,
23 | swapTokens: [],
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/lisk-sepolia/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.LISK_SEPOLIA;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "../lisk/assets/logo.svg",
13 | grayscaleLogoPath: "../lisk/assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://rpc.sepolia-api.lisk.com",
20 | tokens: ["WETH", "ETH"],
21 | enableCCTP: false,
22 | swapTokens: [],
23 | } as ChainConfig;
24 |
--------------------------------------------------------------------------------
/scripts/chain-configs/lisk/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { ChainConfig } from "../types";
3 | import { utils as sdkUtils } from "@across-protocol/sdk";
4 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
5 |
6 | const chainId = CHAIN_IDs.LISK;
7 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
8 |
9 | export default {
10 | ...chainInfoBase,
11 | logoPath: "./assets/logo.svg",
12 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
13 | spokePool: {
14 | address: getDeployedAddress("SpokePool", chainId),
15 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
16 | },
17 | chainId,
18 | blockTimeSeconds: 2,
19 | publicRpcUrl: "https://rpc.api.lisk.com",
20 | tokens: ["WETH", "ETH", "USDC.e", "USDT", "LSK", "WBTC"],
21 | enableCCTP: false,
22 | swapTokens: [],
23 | } as ChainConfig;
24 |
--------------------------------------------------------------------------------
/scripts/chain-configs/mainnet/assets/grayscale-logo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/scripts/chain-configs/mainnet/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/scripts/chain-configs/mode-sepolia/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.MODE_SEPOLIA;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "../mode/assets/logo.svg",
13 | grayscaleLogoPath: "../mode/assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://sepolia.mode.network",
20 | tokens: ["WETH", "ETH"],
21 | enableCCTP: false,
22 | swapTokens: [],
23 | } as ChainConfig;
24 |
--------------------------------------------------------------------------------
/scripts/chain-configs/mode/assets/grayscale-logo.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/scripts/chain-configs/mode/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/scripts/chain-configs/mode/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
4 |
5 | import { ChainConfig } from "../types";
6 |
7 | const chainId = CHAIN_IDs.MODE;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | blockTimeSeconds: 2,
20 | publicRpcUrl: "https://mainnet.mode.network",
21 | tokens: ["WETH", "ETH", "USDC.e", "USDT", "WBTC"],
22 | enableCCTP: false,
23 | swapTokens: [],
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/optimism-sepolia/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.OPTIMISM_SEPOLIA;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "../optimism/assets/logo.svg",
13 | grayscaleLogoPath: "../optimism/assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://sepolia.optimism.io",
20 | tokens: ["ETH", "WETH", "USDC", "XYZ"],
21 | enableCCTP: false,
22 | swapTokens: [],
23 | } as ChainConfig;
24 |
--------------------------------------------------------------------------------
/scripts/chain-configs/optimism/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
4 |
5 | import { ChainConfig } from "../types";
6 |
7 | const chainId = CHAIN_IDs.OPTIMISM;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | publicRpcUrl: "https://mainnet.optimism.io",
19 | chainId,
20 | tokens: [
21 | "WETH",
22 | "ETH",
23 | "USDC",
24 | "WBTC",
25 | "UMA",
26 | "DAI",
27 | "BAL",
28 | "ACX",
29 | "USDT",
30 | "SNX",
31 | "POOL",
32 | "WLD",
33 | ],
34 | blockTimeSeconds: 2,
35 | enableCCTP: true,
36 | } as ChainConfig;
37 |
--------------------------------------------------------------------------------
/scripts/chain-configs/polygon-amoy/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.POLYGON_AMOY;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | fullName: "Polygon Amoy",
13 | logoPath: "../polygon/assets/logo.svg",
14 | grayscaleLogoPath: "../polygon/assets/grayscale-logo.svg",
15 | spokePool: {
16 | address: getDeployedAddress("SpokePool", chainId),
17 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
18 | },
19 | chainId,
20 | publicRpcUrl: "https://rpc-amoy.polygon.technology",
21 | tokens: ["WETH", "TATARA-USDC"],
22 | enableCCTP: false,
23 | swapTokens: [],
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/polygon/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
4 |
5 | import { ChainConfig } from "../types";
6 |
7 | const chainId = CHAIN_IDs.POLYGON;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | fullName: "Polygon Network",
13 | logoPath: "./assets/logo.svg",
14 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
15 | spokePool: {
16 | address: getDeployedAddress("SpokePool", chainId),
17 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
18 | },
19 | chainId,
20 | publicRpcUrl: "https://polygon.drpc.org",
21 | tokens: ["DAI", "UMA", "WETH", "USDC", "WBTC", "BAL", "ACX", "USDT", "POOL"],
22 | enableCCTP: true,
23 | blockTimeSeconds: 5,
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/redstone/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.REDSTONE;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://rpc.redstonechain.com",
20 | blockTimeSeconds: 2,
21 | tokens: ["WETH", "ETH"],
22 | enableCCTP: false,
23 | swapTokens: [],
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/scroll/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.SCROLL;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://rpc.scroll.io",
20 | blockTimeSeconds: 3,
21 | tokens: ["WETH", "ETH", "USDC", "USDT", "WBTC", "POOL"],
22 | enableCCTP: false,
23 | swapTokens: [],
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/sepolia/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.SEPOLIA;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "../mainnet/assets/logo.svg",
13 | grayscaleLogoPath: "../mainnet/assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://gateway.tenderly.co/public/sepolia",
20 | tokens: ["WETH", "ETH", "USDC", "GRASS", "XYZ", "TATARA-USDC"],
21 | enableCCTP: false,
22 | swapTokens: [],
23 | } as ChainConfig;
24 |
--------------------------------------------------------------------------------
/scripts/chain-configs/soneium/assets/grayscale-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/scripts/chain-configs/soneium/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/scripts/chain-configs/soneium/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.SONEIUM;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://rpc.soneium.org",
20 | blockTimeSeconds: 2,
21 | tokens: ["WETH", "ETH", "USDC.e"],
22 | enableCCTP: false,
23 | } as ChainConfig;
24 |
--------------------------------------------------------------------------------
/scripts/chain-configs/tatara/assets/grayscale-logo.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/scripts/chain-configs/tatara/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/scripts/chain-configs/tatara/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.TATARA;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl:
20 | "https://rpc.tatara.katanarpc.com/DYsaaqa6zme7taA8LskCQnkAZghSPtPQk",
21 | blockTimeSeconds: 1,
22 | tokens: ["TATARA-USDC", "WETH"],
23 | enableCCTP: false,
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/types.ts:
--------------------------------------------------------------------------------
1 | export type ChainConfig = {
2 | name: string;
3 | fullName?: string;
4 | nativeToken: string;
5 | blockExplorer: string;
6 | blockTimeSeconds?: number;
7 | publicRpcUrl: string;
8 | chainId: number;
9 | logoPath: string;
10 | grayscaleLogoPath: string;
11 | spokePool: {
12 | address: string;
13 | blockNumber: number;
14 | };
15 | tokens: (
16 | | string
17 | | {
18 | symbol: string;
19 | chainIds: number[];
20 | }
21 | )[];
22 | enableCCTP: boolean;
23 | disabledRoutes?: {
24 | toChainId: number;
25 | fromTokenSymbol: string;
26 | toTokenSymbol: string;
27 | }[];
28 | };
29 |
--------------------------------------------------------------------------------
/scripts/chain-configs/unichain-sepolia/assets/grayscale-logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/scripts/chain-configs/unichain-sepolia/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/scripts/chain-configs/unichain-sepolia/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.UNICHAIN_SEPOLIA;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://sepolia.unichain.org",
20 | blockTimeSeconds: 1,
21 | tokens: ["ETH", "WETH", "USDC"],
22 | enableCCTP: true,
23 | } as ChainConfig;
24 |
--------------------------------------------------------------------------------
/scripts/chain-configs/unichain/assets/grayscale-logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/scripts/chain-configs/unichain/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/scripts/chain-configs/unichain/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.UNICHAIN;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://mainnet.unichain.org",
20 | blockTimeSeconds: 1,
21 | tokens: ["ETH", "WETH", "USDC"],
22 | enableCCTP: true,
23 | } as ChainConfig;
24 |
--------------------------------------------------------------------------------
/scripts/chain-configs/world-chain/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.WORLD_CHAIN;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | fullName: "World Chain",
13 | logoPath: "./assets/logo.svg",
14 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
15 | spokePool: {
16 | address: getDeployedAddress("SpokePool", chainId),
17 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
18 | },
19 | chainId,
20 | publicRpcUrl: "https://worldchain-mainnet.g.alchemy.com/public",
21 | blockTimeSeconds: 2,
22 | tokens: ["ETH", "USDC", "WBTC", "WETH", "POOL", "WLD"],
23 | enableCCTP: false,
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/chain-configs/zk-sync/assets/grayscale-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/scripts/chain-configs/zk-sync/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/scripts/chain-configs/zk-sync/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
4 |
5 | import { ChainConfig } from "../types";
6 |
7 | const chainId = CHAIN_IDs.ZK_SYNC;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | fullName: "zkSync Era",
13 | logoPath: "./assets/logo.svg",
14 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
15 | spokePool: {
16 | address: getDeployedAddress("SpokePool", chainId),
17 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
18 | },
19 | chainId,
20 | blockTimeSeconds: 2,
21 | publicRpcUrl: "https://mainnet.era.zksync.io",
22 | tokens: ["WETH", "ETH", "USDC.e", "WBTC", "USDT", "DAI"],
23 | enableCCTP: false,
24 | swapTokens: [],
25 | } as ChainConfig;
26 |
--------------------------------------------------------------------------------
/scripts/chain-configs/zora/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs, PUBLIC_NETWORKS } from "@across-protocol/constants";
2 | import { utils as sdkUtils } from "@across-protocol/sdk";
3 | import { ChainConfig } from "../types";
4 |
5 | const { getDeployedAddress, getDeployedBlockNumber } = sdkUtils;
6 |
7 | const chainId = CHAIN_IDs.ZORA;
8 | const chainInfoBase = PUBLIC_NETWORKS[chainId];
9 |
10 | export default {
11 | ...chainInfoBase,
12 | logoPath: "./assets/logo.svg",
13 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
14 | spokePool: {
15 | address: getDeployedAddress("SpokePool", chainId),
16 | blockNumber: getDeployedBlockNumber("SpokePool", chainId),
17 | },
18 | chainId,
19 | publicRpcUrl: "https://rpc.zora.energy",
20 | blockTimeSeconds: 2,
21 | tokens: ["WETH", "ETH", "USDzC"],
22 | enableCCTP: false,
23 | swapTokens: [],
24 | } as ChainConfig;
25 |
--------------------------------------------------------------------------------
/scripts/extern-configs/hyperliquid/assets/grayscale-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/scripts/extern-configs/hyperliquid/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/scripts/extern-configs/hyperliquid/index.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs } from "@across-protocol/constants";
2 | import { ExternalProjectConfig } from "../types";
3 |
4 | export default {
5 | name: "Hyperliquid",
6 | projectId: "hyperliquid",
7 | explorer: "https://arbiscan.io",
8 | logoPath: "./assets/logo.svg",
9 | grayscaleLogoPath: "./assets/grayscale-logo.svg",
10 | publicRpcUrl: "https://arbitrum.publicnode.com",
11 | intermediaryChain: CHAIN_IDs.ARBITRUM,
12 | tokens: ["USDC", "USDC.e"],
13 | } as ExternalProjectConfig;
14 |
--------------------------------------------------------------------------------
/scripts/extern-configs/index.ts:
--------------------------------------------------------------------------------
1 | export { default as HYPERLIQUID } from "./hyperliquid";
2 |
--------------------------------------------------------------------------------
/scripts/extern-configs/types.ts:
--------------------------------------------------------------------------------
1 | // Destination only projects that are supported through a message bridge
2 | // at a known supported intermediary chain
3 | export type ExternalProjectConfig = {
4 | projectId: string;
5 | name: string;
6 | fullName?: string;
7 | explorer: string;
8 | publicRpcUrl: string;
9 | logoPath: string;
10 | grayscaleLogoPath: string;
11 | intermediaryChain: number;
12 | tokens: string[];
13 | };
14 |
--------------------------------------------------------------------------------
/scripts/tests/swap-allowance.ts:
--------------------------------------------------------------------------------
1 | import { Wallet } from "ethers";
2 |
3 | import { getProvider } from "../../api/_utils";
4 | import { fetchSwapQuote, signAndWaitAllowanceFlow } from "./_swap-utils";
5 |
6 | async function swapWithAllowance() {
7 | console.log("Swapping with allowance...");
8 | const swapQuote = await fetchSwapQuote("approval");
9 |
10 | if (!swapQuote || !swapQuote.swapTx || !("data" in swapQuote.swapTx)) {
11 | console.log("No swap quote with tx data for approval");
12 | return;
13 | }
14 |
15 | if (process.env.DEV_WALLET_PK) {
16 | const wallet = new Wallet(process.env.DEV_WALLET_PK!).connect(
17 | getProvider(swapQuote.swapTx.chainId)
18 | );
19 |
20 | await signAndWaitAllowanceFlow({ wallet, swapResponse: swapQuote });
21 | }
22 | }
23 |
24 | swapWithAllowance()
25 | .then(() => console.log("Done"))
26 | .catch((e) => {
27 | console.error(e);
28 | if (e.response?.data) {
29 | console.log("Tx for debug sim:", e.response.data.transaction);
30 | }
31 | });
32 |
--------------------------------------------------------------------------------
/scripts/tests/swap-auth.ts:
--------------------------------------------------------------------------------
1 | import { Wallet } from "ethers";
2 |
3 | import { getProvider } from "../../api/_utils";
4 | import { fetchSwapQuote, signAndWaitPermitFlow } from "./_swap-utils";
5 |
6 | async function swapWithAuth() {
7 | console.log("Swapping with auth...");
8 | const swapQuote = await fetchSwapQuote("auth");
9 |
10 | if (!swapQuote || !swapQuote.swapTx || !swapQuote.eip712) {
11 | console.log("No swap quote with EIP712 data for auth");
12 | return;
13 | }
14 |
15 | if (process.env.DEV_WALLET_PK) {
16 | const wallet = new Wallet(process.env.DEV_WALLET_PK!).connect(
17 | getProvider(swapQuote.swapTx.chainId)
18 | );
19 |
20 | await signAndWaitPermitFlow({ wallet, swapResponse: swapQuote });
21 | }
22 | }
23 |
24 | swapWithAuth()
25 | .then(() => console.log("Done"))
26 | .catch((e) => {
27 | console.error(e);
28 | if (e.response?.data) {
29 | console.log("Tx for debug sim:", e.response.data.transaction);
30 | }
31 | });
32 |
--------------------------------------------------------------------------------
/scripts/tests/swap-permit.ts:
--------------------------------------------------------------------------------
1 | import { Wallet } from "ethers";
2 |
3 | import { getProvider } from "../../api/_utils";
4 | import { fetchSwapQuote, signAndWaitPermitFlow } from "./_swap-utils";
5 |
6 | async function swapWithPermit() {
7 | console.log("Swapping with permit...");
8 | const swapQuote = await fetchSwapQuote("permit");
9 |
10 | if (!swapQuote || !swapQuote.swapTx || !swapQuote.eip712) {
11 | console.log("No swap quote with EIP712 data for permit");
12 | return;
13 | }
14 |
15 | if (process.env.DEV_WALLET_PK) {
16 | const wallet = new Wallet(process.env.DEV_WALLET_PK!).connect(
17 | getProvider(swapQuote.swapTx.chainId)
18 | );
19 |
20 | await signAndWaitPermitFlow({ wallet, swapResponse: swapQuote });
21 | }
22 | }
23 |
24 | swapWithPermit()
25 | .then(() => console.log("Done"))
26 | .catch((e) => {
27 | console.error(e);
28 | if (e.response?.data) {
29 | console.log("Tx for debug sim:", e.response.data.transaction);
30 | }
31 | });
32 |
--------------------------------------------------------------------------------
/setup.jest.ts:
--------------------------------------------------------------------------------
1 | import { TextEncoder, TextDecoder } from "util";
2 | global.TextEncoder = TextEncoder;
3 | // @ts-expect-error - The types are incompatible but the implementation works correctly
4 | global.TextDecoder = TextDecoder;
5 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { BrowserRouter as Router } from "react-router-dom";
2 | import Routes from "./Routes";
3 |
4 | function App() {
5 | return (
6 |
7 |
8 |
9 | );
10 | }
11 | export default App;
12 |
--------------------------------------------------------------------------------
/src/assets/bg-banners/stars-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/across-protocol/frontend/234e2fb182d6c5f4e1c7c1ead736f5edfa9c9a66/src/assets/bg-banners/stars-bg.png
--------------------------------------------------------------------------------
/src/assets/chain-logos/base-grayscale.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/base-sepolia-grayscale.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/base-sepolia.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/base.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/blast-grayscale.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/blast-sepolia-grayscale.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/blast-sepolia.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/blast.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/bsc-grayscale.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/bsc.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/doctor-who-grayscale.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/doctor-who.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/linea-grayscale.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/linea.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/mainnet-grayscale.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/mainnet.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/mode-grayscale.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/mode-sepolia-grayscale.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/mode-sepolia.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/mode.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/sepolia-grayscale.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/sepolia.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/soneium-grayscale.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/soneium.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/tatara-grayscale.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/tatara.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/unichain-grayscale.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/unichain-sepolia-grayscale.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/unichain-sepolia.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/unichain.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/zk-sync-grayscale.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/src/assets/chain-logos/zk-sync.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/src/assets/extern-logos/hyperliquid-grayscale.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/extern-logos/hyperliquid.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/arrow-up-down.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/src/assets/icons/arrow-up-right-boxed.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/arrow-up-right.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/check.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/checkmark-circle.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/chevron-down.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/chevron-right.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/clock.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/assets/icons/connectors.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/cross.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/hamburger.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/info.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/plus-circle.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/question-circle.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/referree.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/refresh.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/wallet.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/x-grey.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/src/assets/icons/x-white.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/assets/icons/zap.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/assets/token-logos/acx.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/token-logos/bal.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
18 |
--------------------------------------------------------------------------------
/src/assets/token-logos/bnb.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/token-logos/eth.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/src/assets/token-logos/usdb.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/token-logos/usdt.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Alert/Alert.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | ChildrenWrapper,
3 | StyledInfoIcon,
4 | StyledQuestionIcon,
5 | Wrapper,
6 | } from "./Alert.styles";
7 |
8 | export type AlertStatusType = "base" | "warn" | "danger" | "info";
9 | export type AlertIconType = "info" | "question";
10 |
11 | type AlertProps = {
12 | status: AlertStatusType;
13 | iconType?: AlertIconType;
14 | alignIcon?: "top" | "center";
15 | children: React.ReactNode;
16 | };
17 |
18 | const Alert: React.FC = ({
19 | status,
20 | iconType: _iconType,
21 | alignIcon = "top",
22 | children,
23 | }) => {
24 | const iconType: AlertIconType = _iconType ?? "info";
25 | return (
26 |
27 | {iconType === "info" ? (
28 |
29 | ) : (
30 |
31 | )}
32 | {children}
33 |
34 | );
35 | };
36 |
37 | export default Alert;
38 |
--------------------------------------------------------------------------------
/src/components/Alert/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./Alert";
2 |
--------------------------------------------------------------------------------
/src/components/AmountInput/InputErrorText.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 |
3 | import { Text } from "components/Text";
4 | import { ReactComponent as II } from "assets/icons/info.svg";
5 |
6 | type Props = {
7 | errorText: string;
8 | };
9 |
10 | export function InputErrorText({ errorText }: Props) {
11 | return (
12 |
13 |
14 |
15 | {errorText}
16 |
17 |
18 | );
19 | }
20 |
21 | const ErrorWrapper = styled.div`
22 | display: flex;
23 | flex-direction: row;
24 | justify-content: center;
25 | align-items: center;
26 | padding: 0px;
27 | gap: 8px;
28 |
29 | width: 100%;
30 | `;
31 |
32 | const ErrorIcon = styled(II)`
33 | height: 16px;
34 | width: 16px;
35 |
36 | & path {
37 | stroke: #f96c6c !important;
38 | }
39 | `;
40 |
41 | const ErrorText = styled(Text)`
42 | max-width: 400px;
43 | `;
44 |
--------------------------------------------------------------------------------
/src/components/AmountInput/index.tsx:
--------------------------------------------------------------------------------
1 | export { AmountInput, type Props as AmountInputProps } from "./AmountInput";
2 | export { InputErrorText } from "./InputErrorText";
3 |
--------------------------------------------------------------------------------
/src/components/AmpliTrace/AmpliTrace.tsx:
--------------------------------------------------------------------------------
1 | import { useRouteTrace, useWalletTrace } from "hooks";
2 |
3 | export function AmpliTrace() {
4 | useRouteTrace();
5 | useWalletTrace();
6 |
7 | return <>>;
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/AmpliTrace/index.tsx:
--------------------------------------------------------------------------------
1 | export { AmpliTrace } from "./AmpliTrace";
2 |
--------------------------------------------------------------------------------
/src/components/Badge/Badge.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 |
3 | import { COLORS } from "utils";
4 |
5 | export type BadgeColor = keyof typeof COLORS;
6 |
7 | type BadgeProps = {
8 | borderColor?: BadgeColor;
9 | textColor?: BadgeColor;
10 | };
11 |
12 | export const Badge = styled.div`
13 | display: flex;
14 | height: 20px;
15 | padding: 8px 5px 10px 5px;
16 | justify-content: center;
17 | align-items: center;
18 |
19 | font-variant-numeric: lining-nums tabular-nums;
20 | font-size: 12px;
21 | font-style: normal;
22 | font-weight: 400;
23 | line-height: normal;
24 | letter-spacing: 0.48px;
25 | text-transform: uppercase;
26 |
27 | border-radius: 6px;
28 | border: 1px solid;
29 | border-color: ${({ borderColor, textColor }) =>
30 | COLORS[borderColor || textColor || "white-100"]};
31 | color: ${({ borderColor, textColor }) =>
32 | COLORS[textColor || borderColor || "white-100"]};
33 | `;
34 |
--------------------------------------------------------------------------------
/src/components/Badge/index.tsx:
--------------------------------------------------------------------------------
1 | export { Badge } from "./Badge";
2 |
--------------------------------------------------------------------------------
/src/components/Banner/Banner.styles.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { QUERIES } from "utils";
3 | export const Wrapper = styled.div`
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | padding: 0 30px;
8 | height: 72px;
9 | color: #e0f3ff;
10 | background-color: #202024;
11 | border-bottom: 1px solid #3e4047;
12 | font-size: ${16 / 16}rem;
13 | position: unset;
14 | width: 100%;
15 | top: 0;
16 | left: 0;
17 | z-index: 1100;
18 | @media ${QUERIES.tabletAndDown} {
19 | padding: 0 10px;
20 | }
21 | svg {
22 | margin-right: 16px;
23 | }
24 |
25 | span {
26 | padding: 10px 0;
27 | @media screen and (max-width: 428px) {
28 | font-size: ${14 / 16}rem;
29 | width: 85%;
30 | }
31 | }
32 | `;
33 |
--------------------------------------------------------------------------------
/src/components/Banner/Banner.tsx:
--------------------------------------------------------------------------------
1 | import { useLayoutEffect, useRef } from "react";
2 | import { createPortal } from "react-dom";
3 | import { Wrapper } from "./Banner.styles";
4 | /**
5 | * React component that renders its children in a super header on top of the page.
6 | */
7 | const Banner = ({ children }: { children: React.ReactNode }) => {
8 | const container = useRef(document.getElementById("banner"));
9 | // We create the "super-header" element and insert it into the DOM, if it does not exist already
10 | useLayoutEffect(() => {
11 | if (!container.current) {
12 | // we know this to always be defined.
13 | const root = document.getElementById("root") as HTMLDivElement;
14 | const div = document.createElement("div");
15 | div.id = "banner";
16 | root.insertBefore(div, root.firstChild);
17 | container.current = div;
18 | }
19 | }, []);
20 | if (!container.current) {
21 | return null;
22 | }
23 | return createPortal({children}, container.current);
24 | };
25 |
26 | export default Banner;
27 |
--------------------------------------------------------------------------------
/src/components/Banner/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./Banner";
2 |
--------------------------------------------------------------------------------
/src/components/BouncingDotsLoader/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./BouncingDotsLoader";
2 |
--------------------------------------------------------------------------------
/src/components/BreadcrumbV2/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./BreadcrumbV2";
2 |
--------------------------------------------------------------------------------
/src/components/BreadcrumbV2/useBreadcrumb.ts:
--------------------------------------------------------------------------------
1 | import { useLocation } from "react-router";
2 |
3 | export function useBreadcrumb() {
4 | const location = useLocation();
5 | const routes = location.pathname
6 | .split("/")
7 | .filter((r) => r)
8 | .reduce(
9 | (prev, curr) => {
10 | prev.push({
11 | path: `${prev[prev.length - 1]?.path ?? ""}/${curr}`,
12 | name: curr,
13 | });
14 | return prev;
15 | },
16 | [] as { path: string; name: string }[]
17 | );
18 | if (routes.length === 0) {
19 | routes.push({ path: "/", name: "Home" });
20 | }
21 | const ancestorRoutes = routes.slice(0, -1);
22 | const currentRoute = routes[routes.length - 1];
23 |
24 | return {
25 | routes,
26 | ancestorRoutes,
27 | currentRoute,
28 | };
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/Button/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./Button";
2 |
--------------------------------------------------------------------------------
/src/components/CardWrapper/CardWrapper.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { QUERIESV2 } from "utils";
3 |
4 | const CardWrapper = ({ children }: { children: React.ReactNode }) => (
5 | {children}
6 | );
7 |
8 | export default CardWrapper;
9 |
10 | const Card = styled.div`
11 | width: 100%;
12 |
13 | box-sizing: border-box;
14 | display: flex;
15 | flex-direction: column;
16 | align-items: flex-start;
17 |
18 | background: #34353b;
19 |
20 | border: 1px solid #3e4047;
21 | border-radius: 10px;
22 |
23 | flex-wrap: nowrap;
24 |
25 | padding: 24px;
26 | gap: 24px;
27 | @media ${QUERIESV2.sm.andDown} {
28 | padding: 12px 16px 16px;
29 | gap: 16px;
30 | margin-top: -4px;
31 | }
32 | `;
33 |
--------------------------------------------------------------------------------
/src/components/CardWrapper/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./CardWrapper";
2 |
--------------------------------------------------------------------------------
/src/components/DepositsTable/cells/AmountCell.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 |
3 | import { Text } from "components/Text";
4 | import { Deposit } from "hooks/useDeposits";
5 | import { formatUnitsWithMaxFractions, Token } from "utils";
6 |
7 | import { BaseCell } from "./BaseCell";
8 |
9 | type Props = {
10 | deposit: Deposit;
11 | token: Token;
12 | width: number;
13 | };
14 |
15 | export function AmountCell({ deposit, token, width }: Props) {
16 | const amountToDisplay = deposit.swapTokenAmount || deposit.amount;
17 | return (
18 |
19 |
20 | {formatUnitsWithMaxFractions(amountToDisplay, token.decimals)}
21 |
22 |
23 | );
24 | }
25 |
26 | const StyledAmountCell = styled(BaseCell)`
27 | > div {
28 | text-overflow: ellipsis;
29 | overflow: hidden;
30 | white-space: nowrap;
31 | }
32 | `;
33 |
--------------------------------------------------------------------------------
/src/components/DepositsTable/cells/BaseCell.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 |
3 | export const BaseCell = styled.td<{ width: number }>`
4 | padding: 16px 0px;
5 | display: flex;
6 | flex-direction: row;
7 | align-items: center;
8 | width: ${({ width }) => width}px;
9 |
10 | overflow-x: hidden;
11 | `;
12 |
--------------------------------------------------------------------------------
/src/components/DepositsTable/cells/BridgeFeeCell.tsx:
--------------------------------------------------------------------------------
1 | import { Deposit } from "hooks/useDeposits";
2 |
3 | import { NetFeeCell } from "./NetFeeCell";
4 |
5 | type Props = {
6 | deposit: Deposit;
7 | width: number;
8 | };
9 |
10 | export function BridgeFeeCell({ deposit, width }: Props) {
11 | return ;
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/DepositsTable/cells/DateCell.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { DateTime } from "luxon";
3 |
4 | import { Text } from "components/Text";
5 | import { Deposit } from "hooks/useDeposits";
6 |
7 | import { BaseCell } from "./BaseCell";
8 |
9 | type Props = {
10 | deposit: Deposit;
11 | width: number;
12 | };
13 |
14 | export function DateCell({ deposit, width }: Props) {
15 | return (
16 |
17 |
18 | {DateTime.fromSeconds(deposit.depositTime).toFormat("dd LLL, yyyy")}
19 |
20 |
21 | {DateTime.fromSeconds(deposit.depositTime).toFormat("hh:mm a")}
22 |
23 |
24 | );
25 | }
26 |
27 | const StyledDateCell = styled(BaseCell)`
28 | display: flex;
29 | flex-direction: column;
30 | align-items: flex-start;
31 | `;
32 |
--------------------------------------------------------------------------------
/src/components/DepositsTable/cells/RateCell.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 |
3 | import { Text } from "components/Text";
4 | import { Deposit } from "hooks/useDeposits";
5 |
6 | import { BaseCell } from "./BaseCell";
7 |
8 | type Props = {
9 | deposit: Deposit;
10 | width: number;
11 | };
12 |
13 | export function RateCell({ deposit, width }: Props) {
14 | return (
15 |
16 |
17 | {deposit.rewards ? `${deposit.rewards.rate * 100}%` : "-"}
18 |
19 |
20 | );
21 | }
22 |
23 | const StyledRateCell = styled(BaseCell)`
24 | display: flex;
25 | flex-direction: column;
26 | align-items: flex-start;
27 | `;
28 |
--------------------------------------------------------------------------------
/src/components/DepositsTable/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./DataRow";
2 | export * from "./HeadRow";
3 | export * from "./DepositsTable";
4 | export * from "./PaginatedDepositsTable";
5 |
--------------------------------------------------------------------------------
/src/components/ErrorBoundary/ErrorBoundary.styles.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { QUERIESV2 } from "utils";
3 |
4 | export const Wrapper = styled.div`
5 | display: flex;
6 | flex-direction: column;
7 | justify-content: space-between;
8 | height: 100vh;
9 |
10 | background-color: "#2d2e33";
11 | `;
12 |
13 | export const InnerWrapper = styled.div`
14 | background-color: transparent;
15 |
16 | margin: 0px auto 32px;
17 | display: flex;
18 | flex-direction: column;
19 | justify-content: center;
20 | gap: 16px;
21 | height: 100%;
22 |
23 | padding: 0px 24px;
24 | @media ${QUERIESV2.sm.andDown} {
25 | margin: 0px auto;
26 | padding: 0px 12px;
27 | }
28 | `;
29 |
30 | export const ButtonsWrapper = styled.div`
31 | display: flex;
32 | flex-direction: row;
33 | margin-top: 8px;
34 | @media ${QUERIESV2.sm.andDown} {
35 | flex-direction: column;
36 | }
37 | `;
38 |
--------------------------------------------------------------------------------
/src/components/ErrorBoundary/index.tsx:
--------------------------------------------------------------------------------
1 | export { ErrorBoundary } from "./ErrorBoundary";
2 |
--------------------------------------------------------------------------------
/src/components/ExternalLink/index.ts:
--------------------------------------------------------------------------------
1 | export { ExternalLink } from "./ExternalLink";
2 |
--------------------------------------------------------------------------------
/src/components/Footer/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./Footer";
2 |
--------------------------------------------------------------------------------
/src/components/GlobalStyles/index.ts:
--------------------------------------------------------------------------------
1 | import GlobalStyles from "./GlobalStyles";
2 |
3 | export default GlobalStyles;
4 |
--------------------------------------------------------------------------------
/src/components/Header/MenuToggle.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from "react";
2 | import styled from "@emotion/styled";
3 | import { ReactComponent as HamburgerIcon } from "assets/icons/hamburger.svg";
4 |
5 | interface MenuToggleProps {
6 | toggle: () => void;
7 | }
8 |
9 | const MenuToggle: FC = ({ toggle }) => {
10 | return (
11 | toggle()}>
12 |
13 |
14 | );
15 | };
16 |
17 | export default MenuToggle;
18 |
19 | const CloseButton = styled.button`
20 | position: relative;
21 | width: 40px;
22 | height: 40px;
23 | padding: 0;
24 | margin: 0;
25 | display: flex;
26 | justify-content: center;
27 | align-items: center;
28 | background-color: transparent;
29 | border: 1px solid #4c4e57;
30 | border-radius: 20px;
31 | cursor: pointer;
32 | outline: none;
33 |
34 | :hover {
35 | border: 1px solid #e0f3ff;
36 |
37 | svg rect {
38 | fill: #e0f3ff;
39 | }
40 | }
41 | `;
42 |
--------------------------------------------------------------------------------
/src/components/Header/__tests__/utils.test.ts:
--------------------------------------------------------------------------------
1 | import { isChildPath } from "../utils";
2 |
3 | describe("#isChildPath()", () => {
4 | test("match one level path", () => {
5 | expect(isChildPath("/", "/")).toBeTruthy();
6 | expect(isChildPath("/parent", "/parent")).toBeTruthy();
7 | });
8 |
9 | test("match nested path", () => {
10 | expect(isChildPath("/parent/child", "/parent")).toBeTruthy();
11 | });
12 |
13 | test("do not match wrong path", () => {
14 | expect(isChildPath("/parent/child", "/other-parent")).toBeFalsy();
15 | });
16 |
17 | test("do not match root path", () => {
18 | expect(isChildPath("/parent/child", "/")).toBeFalsy();
19 | });
20 |
21 | test("do not match nested parent path", () => {
22 | expect(isChildPath("/transactions/a/all", "/transactions/b")).toBeFalsy();
23 | expect(
24 | isChildPath("/transactions/all", "/transactions/all/test/subpath")
25 | ).toBeFalsy();
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/src/components/Header/index.ts:
--------------------------------------------------------------------------------
1 | import Header from "./Header";
2 | export default Header;
3 |
--------------------------------------------------------------------------------
/src/components/Header/utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Checks whether the current pathname (possibly nested), matches given parent path.
3 | * @param pathname - Full pathname, e.g. `/transactions/all`
4 | * @param parentPath - Parent path to match against, e.g. `/transactions`
5 | * @returns Match result.
6 | */
7 | export function isChildPath(pathname: string, parentPath: string) {
8 | const splitParentPath = parentPath.split("/");
9 | return splitParentPath.every(
10 | (parentPathElement, i) => pathname.split("/")[i] === parentPathElement
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/IconPair/index.ts:
--------------------------------------------------------------------------------
1 | export { IconPair } from "./IconPair";
2 |
--------------------------------------------------------------------------------
/src/components/LayoutV2/index.ts:
--------------------------------------------------------------------------------
1 | import Layout from "./LayoutV2";
2 | export default Layout;
3 |
--------------------------------------------------------------------------------
/src/components/Loader/Loader.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { Loader as LoaderIcon } from "react-feather";
3 |
4 | type Props = {
5 | size?: number;
6 | };
7 |
8 | export const Loader = styled(LoaderIcon)`
9 | width: ${({ size = 24 }) => size}px;
10 | height: ${({ size = 24 }) => size}px;
11 | animation: rotation 2s infinite linear;
12 |
13 | @keyframes rotation {
14 | from {
15 | transform: rotate(0deg);
16 | }
17 | to {
18 | transform: rotate(359deg);
19 | }
20 | }
21 | `;
22 |
23 | export default Loader;
24 |
--------------------------------------------------------------------------------
/src/components/Loader/index.tsx:
--------------------------------------------------------------------------------
1 | export { Loader } from "./Loader";
2 |
--------------------------------------------------------------------------------
/src/components/LoadingSkeleton/LoadingSkeleton.tsx:
--------------------------------------------------------------------------------
1 | import { keyframes } from "@emotion/react";
2 | import styled from "@emotion/styled";
3 |
4 | const shimmer = keyframes`
5 | to {
6 | background-position-x: 0%
7 | }
8 | `;
9 |
10 | export const LoadingSkeleton = styled.div<{
11 | width?: string;
12 | height?: string;
13 | borderRadius?: string;
14 | }>`
15 | display: flex;
16 | height: ${({ height }) => height || "20px"};
17 | width: ${({ width }) => width || "100%"};
18 | border-radius: ${({ borderRadius }) => borderRadius || "24px"};
19 | background: linear-gradient(
20 | 90deg,
21 | rgba(76, 78, 87, 0) 40%,
22 | #4c4e57 50%,
23 | rgba(76, 78, 87, 0) 60%
24 | );
25 | background-size: 300%;
26 | background-position-x: 100%;
27 | animation: ${shimmer} 1s infinite linear;
28 | `;
29 |
--------------------------------------------------------------------------------
/src/components/LoadingSkeleton/index.tsx:
--------------------------------------------------------------------------------
1 | export { LoadingSkeleton } from "./LoadingSkeleton";
2 |
--------------------------------------------------------------------------------
/src/components/Modal/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./Modal";
2 |
--------------------------------------------------------------------------------
/src/components/Pagination/index.ts:
--------------------------------------------------------------------------------
1 | export { default, PageSizeSelect } from "./PaginationExample";
2 | export { default as paginate } from "./paginate";
3 | export {
4 | Wrapper,
5 | PaginationElements,
6 | ElementWrapper,
7 | PagesPlaceholder,
8 | NextElement,
9 | ArrowIcon,
10 | } from "./Pagination.styles";
11 |
--------------------------------------------------------------------------------
/src/components/ProgressBar/ProgressBar.styles.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 |
3 | export const Wrapper = styled.div`
4 | /* display: flex; */
5 | height: 16px;
6 | width: 100%;
7 | background-color: #2d2e33;
8 | border-radius: 20px;
9 | border: 1px solid #fff;
10 | `;
11 |
12 | interface IStyledProgress {
13 | width: number;
14 | }
15 |
16 | export const StyledProgress = styled.div`
17 | height: 10px;
18 | width: ${({ width }) => {
19 | return `${width}%`;
20 | }};
21 | background-color: #ffffff;
22 | border-radius: 20px;
23 | text-align: right;
24 | padding: ${({ width }) => {
25 | return width > 0 ? "2px" : "0px";
26 | }};
27 | margin: 2px;
28 | `;
29 |
--------------------------------------------------------------------------------
/src/components/ProgressBar/ProgressBar.tsx:
--------------------------------------------------------------------------------
1 | import { Wrapper, StyledProgress } from "./ProgressBar.styles";
2 |
3 | interface Props {
4 | percent: number;
5 | className?: string;
6 | }
7 |
8 | const ProgressBar: React.FC = ({ percent, className }) => {
9 | return (
10 |
11 |
12 |
13 | );
14 | };
15 |
16 | export default ProgressBar;
17 |
--------------------------------------------------------------------------------
/src/components/ProgressBar/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./ProgressBar";
2 |
--------------------------------------------------------------------------------
/src/components/RewardTable/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./RewardTable";
2 |
--------------------------------------------------------------------------------
/src/components/ScrollToTop/ScrollToTop.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useLocation } from "react-router-dom";
3 |
4 | const ScrollToTop = () => {
5 | const { pathname } = useLocation();
6 |
7 | useEffect(() => {
8 | window.scrollTo(0, 0);
9 | }, [pathname]);
10 |
11 | return null;
12 | };
13 |
14 | export default ScrollToTop;
15 |
--------------------------------------------------------------------------------
/src/components/ScrollToTop/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./ScrollToTop";
2 |
--------------------------------------------------------------------------------
/src/components/SectionTitleWrapperV2/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./SectionWrapperV2";
2 |
--------------------------------------------------------------------------------
/src/components/Selector/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./Selector";
2 |
--------------------------------------------------------------------------------
/src/components/Selector/useSelector.ts:
--------------------------------------------------------------------------------
1 | import useCurrentBreakpoint from "hooks/useCurrentBreakpoint";
2 | import { useState } from "react";
3 | import { SelectorElementType } from "./Selector";
4 | import { isEqual } from "lodash-es";
5 |
6 | export function useSelector(
7 | elements: SelectorElementType[],
8 | selectedValue: ValueType
9 | ) {
10 | const [displayModal, setDisplayModal] = useState(false);
11 | const selectedIndex = elements.findIndex((element) =>
12 | isEqual(element.value, selectedValue)
13 | );
14 | const { isMobile } = useCurrentBreakpoint();
15 |
16 | return {
17 | displayModal,
18 | setDisplayModal,
19 | selectedIndex: selectedIndex < 0 ? 0 : selectedIndex,
20 | isMobile,
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/Sidebar/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./Sidebar";
2 |
--------------------------------------------------------------------------------
/src/components/SuperHeader/index.ts:
--------------------------------------------------------------------------------
1 | import SuperHeader from "./SuperHeader";
2 | export default SuperHeader;
3 |
--------------------------------------------------------------------------------
/src/components/Table/Table.d.ts:
--------------------------------------------------------------------------------
1 | export interface ICell {
2 | value: string | JSX.Element;
3 | }
4 |
5 | export interface IRow {
6 | cells: ICell[];
7 | explorerLink?: JSX.Element;
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/Table/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./TableExample";
2 | export {
3 | BaseTableWrapper,
4 | BaseWrapper,
5 | BaseTitle,
6 | BaseEmptyRow,
7 | BaseTableHeadRow,
8 | BaseTableBody,
9 | BaseTableRow,
10 | BaseHeadCell,
11 | BaseTableCell,
12 | } from "./Table.styles";
13 |
--------------------------------------------------------------------------------
/src/components/Tabs/index.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 |
3 | import { QUERIESV2 } from "utils";
4 |
5 | export const Tabs = styled.div`
6 | display: flex;
7 | justify-content: center;
8 | width: 100%;
9 | margin: 0 auto 0px;
10 | justify-items: center;
11 | `;
12 |
13 | interface ITab {
14 | active: boolean;
15 | }
16 | export const Tab = styled.div`
17 | flex-grow: 1;
18 | text-align: center;
19 | padding: 0 0 20px;
20 | border-bottom: ${(props) =>
21 | props.active ? "2px solid #e0f3ff" : "1px solid #3E4047"};
22 | cursor: pointer;
23 | color: ${(props) => (props.active ? "#E0F3FF" : "#9DAAB2")};
24 |
25 | @media ${QUERIESV2.sm.andDown} {
26 | padding: 0 0 12px;
27 | }
28 | `;
29 |
--------------------------------------------------------------------------------
/src/components/Text/index.ts:
--------------------------------------------------------------------------------
1 | export { Text } from "./Text";
2 | export type { TextColor, TextSize } from "./Text";
3 |
--------------------------------------------------------------------------------
/src/components/Toast/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./Toast";
2 |
--------------------------------------------------------------------------------
/src/components/Toast/toast.d.ts:
--------------------------------------------------------------------------------
1 | export type ToastPosition =
2 | | "top-right"
3 | | "top-left"
4 | | "bottom-right"
5 | | "bottom-left";
6 |
7 | export type ToastType = "success" | "info" | "warning" | "error";
8 | export type IconSize = "sm" | "md" | undefined;
9 |
10 | export interface ToastProperties {
11 | id: string;
12 | type: ToastType;
13 | title: string;
14 | body: string;
15 | createdAt: number;
16 | iconSize?: IconSize;
17 | // Allow any type of component to be passed if you need to render more than the title or body
18 | comp?: React.ReactElement;
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/Tooltip/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./Tooltip";
2 |
--------------------------------------------------------------------------------
/src/components/Wallet/index.ts:
--------------------------------------------------------------------------------
1 | import Wallet from "./Wallet";
2 | export default Wallet;
3 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export { default as GlobalStyles } from "./GlobalStyles";
2 | export { default as Header } from "./Header";
3 | export { default as SuperHeader } from "./SuperHeader";
4 | export { default as Banner } from "./Banner";
5 | export { default as Sidebar } from "./Sidebar";
6 | export { default as LayoutV2 } from "./LayoutV2";
7 | export { default as Modal } from "./Modal";
8 | export { default as Alert } from "./Alert";
9 | export { default as Selector } from "./Selector";
10 | export { Text } from "./Text";
11 | export { default as CardWrapper } from "./CardWrapper";
12 | export { LoadingSkeleton } from "./LoadingSkeleton";
13 |
14 | export * from "./AmountInput";
15 | export * from "./Button";
16 | export * from "./Badge";
17 | export * from "./ExternalLink";
18 | export * from "./ErrorBoundary";
19 |
--------------------------------------------------------------------------------
/src/constants/chains/utils.ts:
--------------------------------------------------------------------------------
1 | export const vercelApiBaseUrl =
2 | process.env.REACT_APP_VERCEL_API_BASE_URL_OVERRIDE || "";
3 |
4 | export function getProxyRpcUrl(chainId: number): string {
5 | return `${vercelApiBaseUrl}/api/rpc-proxy?chainId=${chainId}`;
6 | }
7 |
--------------------------------------------------------------------------------
/src/constants/pools.ts:
--------------------------------------------------------------------------------
1 | import { CHAIN_IDs } from "@across-protocol/constants";
2 | import { TokenInfo, orderedTokenLogos } from "./tokens";
3 |
4 | export type ExternalLPTokenList = Array<
5 | TokenInfo & {
6 | provider: string;
7 | linkToLP: string;
8 | }
9 | >;
10 |
11 | export const externalLPsForStaking: Record = {
12 | [CHAIN_IDs.MAINNET]: [
13 | {
14 | name: "Balancer 50wstETH-50ACX",
15 | symbol: "50wstETH-50ACX",
16 | displaySymbol: "50wstETH-50ACX",
17 | decimals: 18,
18 | mainnetAddress: "0x36Be1E97eA98AB43b4dEBf92742517266F5731a3",
19 | logoURI: orderedTokenLogos.BAL,
20 | provider: "balancer",
21 | linkToLP:
22 | "https://app.balancer.fi/#/ethereum/pool/0x36be1e97ea98ab43b4debf92742517266f5731a3000200000000000000000466",
23 | logoURIs: [
24 | orderedTokenLogos.ACX,
25 | "https://assets.coingecko.com/coins/images/18834/small/wstETH.png?1633565443",
26 | ],
27 | },
28 | ],
29 | [CHAIN_IDs.SEPOLIA]: [],
30 | };
31 |
--------------------------------------------------------------------------------
/src/custom.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.svg" {
2 | import React = require("react");
3 | export const ReactComponent: React.FC>;
4 | const src: string;
5 | export default src;
6 | }
7 |
--------------------------------------------------------------------------------
/src/data/examples/dynamic-weights.json:
--------------------------------------------------------------------------------
1 | {
2 | "0x07aE8551Be970cB1cCa11Dd7a11F47Ae82e70E67": 0.1,
3 | "0x15652636f3898F550b257B89926d5566821c32E1": 0.75,
4 | "0x41ee28EE05341E7fdDdc8d433BA66054Cd302cA1": 0.75,
5 | "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D": 0.25,
6 | "0xCad97616f91872C02BA3553dB315Db4015cBE850": 0.75
7 | }
8 |
--------------------------------------------------------------------------------
/src/data/examples/exclusive-relayers.json:
--------------------------------------------------------------------------------
1 | {
2 | "0x15652636f3898F550b257B89926d5566821c32E1": {
3 | "minExclusivityPeriod": 3,
4 | "minProfitThreshold": 0.00005,
5 | "balanceMultiplier": 0.2,
6 | "maxFillSize": 2500,
7 | "originChainIds": [10]
8 | },
9 | "0x41ee28EE05341E7fdDdc8d433BA66054Cd302cA1": {
10 | "minExclusivityPeriod": 3,
11 | "minProfitThreshold": 0.00005,
12 | "balanceMultiplier": 0.2,
13 | "maxFillSize": 2500,
14 | "originChainIds": [8453]
15 | },
16 | "0xCad97616f91872C02BA3553dB315Db4015cBE850": {
17 | "minExclusivityPeriod": 3,
18 | "minProfitThreshold": 0.00005,
19 | "balanceMultiplier": 0.2,
20 | "maxFillSize": 2500,
21 | "originChainIds": [42161]
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/data/examples/exclusivity-fill-times.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "destination_route_classification": "1,10,324",
4 | "max_size_usd": "1000",
5 | "origin_route_classification": "1,10,324",
6 | "p75_fill_time_secs": "3",
7 | "token_liquidity_groups": "ETH,WETH"
8 | },
9 | {
10 | "destination_route_classification": "0",
11 | "max_size_usd": "100000000",
12 | "origin_route_classification": "0",
13 | "p75_fill_time_secs": "3",
14 | "token_liquidity_groups": "OTHER"
15 | }
16 | ]
17 |
--------------------------------------------------------------------------------
/src/data/examples/exclusivity-strategy.json:
--------------------------------------------------------------------------------
1 | {
2 | "default": "none",
3 | "tokens": {
4 | "WETH": {
5 | "default": "none",
6 | "destinationChainIds": {
7 | "137": { "strategy": "weightedRandom", "weight": 0.05 }
8 | }
9 | },
10 | "ETH": {
11 | "default": "none",
12 | "destinationChainIds": {
13 | "137": { "strategy": "weightedRandom", "weight": 0.05 }
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/data/examples/fill-times.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "destination_route_classification": "1,10,324",
4 | "max_size_usd": "1000",
5 | "origin_route_classification": "1,10,324",
6 | "p75_fill_time_secs": "3",
7 | "token_liquidity_groups": "ETH,WETH"
8 | },
9 | {
10 | "destination_route_classification": "0",
11 | "max_size_usd": "100000000",
12 | "origin_route_classification": "0",
13 | "p75_fill_time_secs": "3",
14 | "token_liquidity_groups": "OTHER"
15 | }
16 | ]
17 |
--------------------------------------------------------------------------------
/src/data/examples/fixed-weights.json:
--------------------------------------------------------------------------------
1 | {
2 | "0x07aE8551Be970cB1cCa11Dd7a11F47Ae82e70E67": 0.1,
3 | "0x15652636f3898F550b257B89926d5566821c32E1": 1,
4 | "0x41ee28EE05341E7fdDdc8d433BA66054Cd302cA1": 1,
5 | "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D": 0.1,
6 | "0xCad97616f91872C02BA3553dB315Db4015cBE850": 1
7 | }
8 |
--------------------------------------------------------------------------------
/src/data/examples/rpc-providers.json:
--------------------------------------------------------------------------------
1 | {
2 | "providers": {
3 | "enabled": {
4 | "default": ["public"]
5 | },
6 | "urls": {
7 | "public": {
8 | "1": "https://mainnet.gateway.tenderly.co",
9 | "10": "https://mainnet.optimism.io",
10 | "137": "https://polygon-mainnet.g.alchemy.com/v2/demo",
11 | "324": "https://mainnet.era.zksync.io",
12 | "690": "https://rpc.redstonechain.com",
13 | "8453": "https://mainnet.base.org",
14 | "34443": "https://mainnet.mode.network",
15 | "42161": "https://arb1.arbitrum.io/rpc",
16 | "59144": "https://rpc.linea.build",
17 | "81457": "https://rpc.blast.io",
18 | "534352": "https://rpc.scroll.io",
19 | "7777777": "https://rpc.zora.energy"
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./useBalance";
2 | export * from "./useBridgeFees";
3 | export * from "./useBridgeLimits";
4 | export * from "./usePrevious";
5 | export * from "./useQueryParams";
6 | export * from "./useError";
7 | export * from "./useWindowSize";
8 | export * from "./useScrollPosition";
9 | export * from "./useCenteredInViewport";
10 | export * from "./useConnection";
11 | export * from "./useIsWrongNetwork";
12 | export * from "./useStakingPool";
13 | export * from "./useApprove";
14 | export * from "./useWalletBalanceTrace";
15 | export * from "./useRouteTrace";
16 | export * from "./useWalletTrace";
17 | export * from "./useQueue";
18 | export * from "./useAmplitude";
19 | export * from "./useRewardSummary";
20 |
--------------------------------------------------------------------------------
/src/hooks/tests/usePrevious.test.ts:
--------------------------------------------------------------------------------
1 | import { renderHook } from "@testing-library/react";
2 | import { usePrevious } from "../usePrevious";
3 | const setUp = () =>
4 | renderHook(({ state }) => usePrevious(state), { initialProps: { state: 0 } });
5 |
6 | it("should return 0 on initial render", () => {
7 | const { result } = setUp();
8 |
9 | expect(result.current).toEqual(0);
10 | });
11 |
12 | it("should always return previous state after each update", () => {
13 | const { result, rerender } = setUp();
14 |
15 | rerender({ state: 2 });
16 | expect(result.current).toBe(0);
17 |
18 | rerender({ state: 4 });
19 | expect(result.current).toBe(2);
20 |
21 | rerender({ state: 6 });
22 | expect(result.current).toBe(4);
23 | });
24 |
--------------------------------------------------------------------------------
/src/hooks/tests/useScrollPosition.test.ts:
--------------------------------------------------------------------------------
1 | import { renderHook } from "@testing-library/react";
2 | import useScrollPosition from "../useScrollPosition";
3 |
4 | test("should have a default scroll position", () => {
5 | const { result } = renderHook(() => useScrollPosition());
6 | expect(result.current).toEqual(0);
7 | });
8 |
9 | // TODO fix this test
10 |
11 | // test("scrollPosition updates on change", () => {
12 | // const { result } = renderHook(() => useScrollPosition());
13 |
14 | // act(() => {
15 | // global.scrollY = 1000;
16 | // // Trigger the window resize event.
17 | // global.dispatchEvent(new Event("scroll"));
18 | // });
19 | // expect(result.current).toEqual(100);
20 | // });
21 |
--------------------------------------------------------------------------------
/src/hooks/tests/useWindowSize.test.ts:
--------------------------------------------------------------------------------
1 | import { renderHook, act } from "@testing-library/react";
2 | import useWindowSize from "../useWindowSize";
3 |
4 | test("should have a default window size", () => {
5 | const { result } = renderHook(() => useWindowSize());
6 | expect(result.current.width).toEqual(1024);
7 | expect(result.current.height).toEqual(768);
8 | });
9 |
10 | test("Resize should change default width and height", () => {
11 | const { result } = renderHook(() => useWindowSize());
12 |
13 | act(() => {
14 | global.innerWidth = 1000;
15 | global.innerHeight = 500;
16 | // Trigger the window resize event.
17 | global.dispatchEvent(new Event("resize"));
18 | });
19 | expect(result.current.width).toEqual(1000);
20 | expect(result.current.height).toEqual(500);
21 | });
22 |
--------------------------------------------------------------------------------
/src/hooks/useAmpliTracking.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 |
3 | import { useQueue } from "hooks/useQueue";
4 |
5 | export type TrackingRequest = () => Promise | void;
6 |
7 | export function useAmpliTracking(areInitialPropsSet: boolean) {
8 | const { addToQueue, queue, processNext } = useQueue();
9 |
10 | useEffect(() => {
11 | if (queue.length > 0 && areInitialPropsSet) {
12 | processNext(async (nextRequest) => await nextRequest());
13 | }
14 | }, [queue, processNext, areInitialPropsSet]);
15 |
16 | return { addToQueue, queue };
17 | }
18 |
--------------------------------------------------------------------------------
/src/hooks/useCenteredInViewport.ts:
--------------------------------------------------------------------------------
1 | import { useState, useMemo, useEffect } from "react";
2 |
3 | export function useCenteredInViewport(
4 | ref: React.MutableRefObject
5 | ) {
6 | const [isIntersecting, setIsIntersecting] = useState(false);
7 |
8 | const observer = useMemo(
9 | () =>
10 | new IntersectionObserver(
11 | ([entry]) => setIsIntersecting(entry.isIntersecting),
12 | {
13 | /**
14 | * This rootMargin creates a horizontal line vertically centered
15 | * that will help trigger an intersection at that y point.
16 | */
17 | rootMargin: "-50% 0% -50% 0%",
18 | }
19 | ),
20 | []
21 | );
22 |
23 | useEffect(() => {
24 | if (ref.current !== null) {
25 | observer.observe(ref.current);
26 | }
27 |
28 | return () => {
29 | observer.disconnect();
30 | };
31 | }, [ref, observer]);
32 |
33 | return isIntersecting;
34 | }
35 |
--------------------------------------------------------------------------------
/src/hooks/useClickOutsideModal.ts:
--------------------------------------------------------------------------------
1 | import { MutableRefObject, useEffect } from "react";
2 |
3 | export default function useClickOutsideModal(
4 | ref: MutableRefObject,
5 | callback: () => void
6 | ) {
7 | useEffect(() => {
8 | function handleClickOutside(event: Event) {
9 | if (ref.current && !ref.current.contains(event.target)) {
10 | callback();
11 | }
12 | }
13 | // Bind the event listener
14 | document.addEventListener("mousedown", handleClickOutside);
15 | return () => {
16 | // Unbind the event listener on clean up
17 | document.removeEventListener("mousedown", handleClickOutside);
18 | };
19 | }, [ref, callback]);
20 | }
21 |
--------------------------------------------------------------------------------
/src/hooks/useCoingeckoPrice.ts:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 | import getApiEndpoint from "utils/serverless-api";
3 |
4 | export function useCoingeckoPrice(
5 | l1Token: string,
6 | baseCurrency: string,
7 | historicalDateISO?: string,
8 | enabled: boolean = true
9 | ) {
10 | return useQuery({
11 | queryKey: ["price", historicalDateISO ?? "current", l1Token, baseCurrency],
12 | queryFn: async () =>
13 | getApiEndpoint().coingecko(l1Token, baseCurrency, historicalDateISO),
14 | enabled,
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/src/hooks/useConnection.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from "ethers";
2 |
3 | import { useOnboard } from "hooks/useOnboard";
4 | import { useIsContractAddress } from "hooks/useIsContractAddress";
5 |
6 | export function useConnection() {
7 | const {
8 | provider,
9 | signer,
10 | isConnected,
11 | connect,
12 | disconnect,
13 | account,
14 | chainId,
15 | wallet,
16 | error,
17 | setChain,
18 | didAttemptAutoSelect,
19 | } = useOnboard();
20 |
21 | const isContractAddress = useIsContractAddress(
22 | account?.address,
23 | chainId,
24 | true
25 | );
26 |
27 | return {
28 | account: account ? ethers.utils.getAddress(account.address) : undefined,
29 | ensName: account?.ens?.name,
30 | chainId,
31 | provider,
32 | signer,
33 | isConnected,
34 | connect,
35 | disconnect,
36 | error,
37 | wallet,
38 | setChain,
39 | isContractAddress,
40 | didAttemptAutoSelect,
41 | };
42 | }
43 |
--------------------------------------------------------------------------------
/src/hooks/useEns.ts:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 |
3 | import { ChainId, getProvider, isDefined } from "utils";
4 |
5 | export function useEnsQuery(address?: string) {
6 | const result = useQuery({
7 | queryKey: ["ens", address] as [string, string],
8 | queryFn: async ({ queryKey }) => {
9 | const [, addressToQuery] = queryKey;
10 | const provider = getProvider(ChainId.MAINNET);
11 | const [ensName, avatar] = await Promise.all([
12 | provider.lookupAddress(addressToQuery),
13 | provider.getAvatar(addressToQuery),
14 | ]);
15 | return { ensName, avatar };
16 | },
17 | staleTime: Infinity,
18 | enabled: isDefined(address),
19 | });
20 |
21 | const resolvedData = isDefined(result.data)
22 | ? result.data
23 | : { ensName: null, avatar: null };
24 |
25 | return {
26 | ...result,
27 | data: resolvedData,
28 | };
29 | }
30 |
--------------------------------------------------------------------------------
/src/hooks/usePageScrollLock.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect } from "react";
2 |
3 | export default function usePageScrollLock() {
4 | const unlockScroll = useCallback(() => {
5 | document.body.style.overflow = "unset";
6 | }, []);
7 |
8 | const lockScroll = useCallback(() => {
9 | document.body.style.overflow = "hidden";
10 | }, []);
11 |
12 | useEffect(() => {
13 | return () => unlockScroll();
14 | }, [unlockScroll]);
15 |
16 | return {
17 | lockScroll,
18 | unlockScroll,
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/src/hooks/usePrevious.ts:
--------------------------------------------------------------------------------
1 | // For getting the ref of a value.
2 | // If you need to know what the previous value of any state variable you're tracking
3 | // Pass it into here and you can do a comparison to the current value.
4 | // IE: const prevValue = useRef(value) === value ? x : y;
5 |
6 | import { useEffect, useRef } from "react";
7 |
8 | /**
9 | * For getting the ref of a value.
10 | *
11 | * If you need to know what the previous value of any state variable you're tracking, pass it into here and you can do a comparison to the current value.
12 | * @param value The value to track
13 | * @returns The value at the previous render.
14 | * @example const prevValue = useRef(value) === value ? x : y;
15 | */
16 | export function usePrevious(value: T): T {
17 | const ref = useRef(value);
18 | useEffect(() => {
19 | ref.current = value;
20 | });
21 | return ref.current;
22 | }
23 |
--------------------------------------------------------------------------------
/src/hooks/useQueryParams.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from "react";
2 | import { useLocation } from "react-router-dom";
3 |
4 | export function useQueryParams() {
5 | const { search } = useLocation();
6 | return useMemo(() => {
7 | const params = new URLSearchParams(search);
8 | return Object.fromEntries(params.entries());
9 | }, [search]);
10 | }
11 |
--------------------------------------------------------------------------------
/src/hooks/useQueue.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useState } from "react";
2 |
3 | export function useQueue() {
4 | const [queue, setQueue] = useState([]);
5 |
6 | const addToQueue = useCallback((item: T) => {
7 | setQueue((prevQueue) => [...prevQueue, item]);
8 | }, []);
9 |
10 | const processNext = useCallback(
11 | async (processFn: (item: T) => Promise) => {
12 | const [next, ...rest] = queue;
13 |
14 | if (!next) {
15 | return;
16 | }
17 |
18 | await processFn(next);
19 | setQueue(rest);
20 | return next;
21 | },
22 | [queue]
23 | );
24 |
25 | return {
26 | queue,
27 | addToQueue,
28 | processNext,
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/src/hooks/useReferralLink.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from "react";
2 | import { shortenString, resolveWebsiteUrl } from "utils";
3 | import { useConnection } from "./useConnection";
4 |
5 | export function useReferralLink() {
6 | const { account } = useConnection();
7 | return useMemo(() => {
8 | const protocolUrl = resolveWebsiteUrl();
9 | const domain = new URL(protocolUrl).hostname;
10 | const addRef = (url: string, r?: string) =>
11 | `${url}?ref=${r ?? account ?? ""}`;
12 | return {
13 | referralLink: addRef(domain),
14 | referralLinkWithProtocol: addRef(protocolUrl),
15 | condensedReferralLink: addRef(
16 | domain,
17 | shortenString(account ?? "", "..", 4)
18 | ),
19 | };
20 | }, [account]);
21 | }
22 |
--------------------------------------------------------------------------------
/src/hooks/useScrollElementByHashIntoView.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useLocation } from "react-router-dom";
3 |
4 | export function useScrollElementByHashIntoView() {
5 | const { hash } = useLocation();
6 |
7 | useEffect(() => {
8 | if (hash) {
9 | const element = document.getElementById(hash.replaceAll("#", ""));
10 | if (element) {
11 | element.scrollIntoView({
12 | behavior: "smooth",
13 | });
14 | }
15 | }
16 | // eslint-disable-next-line
17 | }, []);
18 |
19 | return null;
20 | }
21 |
22 | export default useScrollElementByHashIntoView;
23 |
--------------------------------------------------------------------------------
/src/hooks/useScrollPosition.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import throttle from "lodash/throttle";
3 |
4 | const useScrollPosition = () => {
5 | const [scrollPosition, setScrollPosition] = useState(0);
6 |
7 | useEffect(() => {
8 | const updatePosition = throttle(() => {
9 | setScrollPosition(window.scrollY);
10 | }, 50);
11 | window.addEventListener("scroll", updatePosition);
12 | updatePosition();
13 | return () => window.removeEventListener("scroll", updatePosition);
14 | }, []);
15 |
16 | return scrollPosition;
17 | };
18 |
19 | export default useScrollPosition;
20 |
--------------------------------------------------------------------------------
/src/mp4.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.mp4" {
2 | const src: string;
3 | export default src;
4 | }
5 |
--------------------------------------------------------------------------------
/src/onboard-override.css:
--------------------------------------------------------------------------------
1 | /* Override root variables to style the Onboard modal */
2 | :root {
3 | --w3o-background-color: #2e2e34;
4 | --w3o-foreground-color: #34353b;
5 | --w3o-text-color: #e0f3ff;
6 | --w3o-border-color: #3b3d43;
7 | --w3o-font-family: Barlow;
8 | }
9 |
--------------------------------------------------------------------------------
/src/stories/Alert.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentStory, ComponentMeta } from "@storybook/react";
2 | import Alert from "components/Alert";
3 |
4 | export default {
5 | title: "Alert",
6 | component: Alert,
7 | argTypes: {},
8 | } as ComponentMeta;
9 |
10 | const Template: ComponentStory = (args) => {
11 | return {args.children};
12 | };
13 |
14 | export const Default = Template.bind({});
15 | Default.args = {
16 | children: This is a warning
,
17 | };
18 |
--------------------------------------------------------------------------------
/src/stories/Badge.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from "@storybook/react";
2 |
3 | import { Badge } from "../components/Badge";
4 |
5 | const meta: Meta = {
6 | component: Badge,
7 | argTypes: {
8 | borderColor: {
9 | control: {
10 | type: "select",
11 | },
12 | },
13 | textColor: {
14 | control: {
15 | type: "select",
16 | },
17 | },
18 | },
19 | };
20 |
21 | export default meta;
22 |
23 | type Story = StoryObj;
24 |
25 | export const DefaultBadge: Story = {
26 | render: (args) => (
27 | <>
28 | 1 / 2
29 | >
30 | ),
31 | };
32 |
--------------------------------------------------------------------------------
/src/stories/BouncingDotsLoader.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentStory, ComponentMeta } from "@storybook/react";
2 | import BouncingDotsLoader from "components/BouncingDotsLoader";
3 |
4 | export default {
5 | title: "BouncingDotsLoader",
6 | component: BouncingDotsLoader,
7 | argTypes: {
8 | type: {
9 | values: ["default", "big"],
10 | control: {
11 | type: "radio",
12 | },
13 | },
14 | },
15 | } as ComponentMeta;
16 |
17 | const Template: ComponentStory = (args) => {
18 | const styles = {
19 | display: "flex",
20 | justifyContent: "center",
21 | alignItems: "center",
22 | width: "200px",
23 | height: "60px",
24 | backgroundColor: "green",
25 | };
26 | return (
27 |
28 |
29 |
30 | );
31 | };
32 |
33 | export const Default = Template.bind({});
34 | Default.args = {};
35 |
36 | export const WhiteIcons = Template.bind({});
37 | WhiteIcons.args = {
38 | dotColor: "white",
39 | };
40 |
--------------------------------------------------------------------------------
/src/stories/Footer.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentStory, ComponentMeta } from "@storybook/react";
2 | import Footer from "components/Footer";
3 |
4 | // More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
5 | export default {
6 | title: "Footer",
7 | component: Footer,
8 | // More on argTypes: https://storybook.js.org/docs/react/api/argtypes
9 | argTypes: {},
10 | } as ComponentMeta;
11 |
12 | // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
13 | const Template: ComponentStory = () => ;
14 |
15 | export const Default = Template.bind({});
16 | // More on args: https://storybook.js.org/docs/react/writing-stories/args
17 | Default.args = {};
18 |
--------------------------------------------------------------------------------
/src/stories/buttons/PrimaryButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from "@storybook/react";
2 |
3 | import { PrimaryButton } from "../../components/Button";
4 |
5 | const meta: Meta = {
6 | component: PrimaryButton,
7 | argTypes: {
8 | backgroundColor: {
9 | control: {
10 | type: "select",
11 | },
12 | },
13 | textColor: {
14 | control: {
15 | type: "select",
16 | },
17 | },
18 | size: {
19 | control: {
20 | type: "radio",
21 | options: ["lg", "md", "sm"],
22 | },
23 | },
24 | disabled: {
25 | control: {
26 | type: "boolean",
27 | },
28 | },
29 | },
30 | };
31 |
32 | export default meta;
33 |
34 | type Story = StoryObj;
35 |
36 | export const Primary: Story = {
37 | render: (args) => Primary Button,
38 | };
39 |
--------------------------------------------------------------------------------
/src/stories/buttons/SecondaryButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from "@storybook/react";
2 |
3 | import { SecondaryButton } from "../../components/Button";
4 |
5 | const meta: Meta = {
6 | component: SecondaryButton,
7 | argTypes: {
8 | borderColor: {
9 | control: {
10 | type: "select",
11 | },
12 | },
13 | hoveredBorderColor: {
14 | control: {
15 | type: "select",
16 | },
17 | },
18 | textColor: {
19 | control: {
20 | type: "select",
21 | },
22 | },
23 | size: {
24 | control: {
25 | type: "radio",
26 | options: ["lg", "md", "sm"],
27 | },
28 | },
29 | disabled: {
30 | control: {
31 | type: "boolean",
32 | },
33 | },
34 | },
35 | };
36 |
37 | export default meta;
38 |
39 | type Story = StoryObj;
40 |
41 | export const Secondary: Story = {
42 | render: (args) => (
43 | Secondary Button
44 | ),
45 | };
46 |
--------------------------------------------------------------------------------
/src/utils/address.ts:
--------------------------------------------------------------------------------
1 | import { utils } from "ethers";
2 |
3 | import { ChainId } from "./constants";
4 | import { getProvider } from "./providers";
5 |
6 | export function isValidAddress(address: string) {
7 | return utils.isAddress(address);
8 | }
9 |
10 | export function getAddress(address: string) {
11 | return utils.getAddress(address);
12 | }
13 |
14 | export const noContractCode = "0x";
15 | export async function getCode(address: string, chainId: ChainId) {
16 | const provider = getProvider(chainId);
17 | return await provider.getCode(address);
18 | }
19 |
--------------------------------------------------------------------------------
/src/utils/bignumber.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber, BigNumberish } from "ethers";
2 |
3 | export function bigNumberifyObject(obj: Record): T {
4 | return Object.fromEntries(
5 | Object.entries(obj as Record).map(([key, value]) => {
6 | if (isBigNumberish(value)) {
7 | return [key, BigNumber.from(value)];
8 | }
9 |
10 | if (typeof value === "object" && value !== null) {
11 | return [key, bigNumberifyObject(value as Record)];
12 | }
13 |
14 | return [key, value];
15 | })
16 | ) as T;
17 | }
18 |
19 | export function isBigNumberish(value: unknown): value is BigNumberish {
20 | try {
21 | BigNumber.from(value);
22 | return true;
23 | } catch (e) {
24 | return false;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/utils/convertdecimals.ts:
--------------------------------------------------------------------------------
1 | import assert from "assert";
2 | import { BigNumber, BigNumberish } from "ethers";
3 |
4 | /**
5 | * Convert a number from one decimal place to another.
6 | * @param fromDecimals - The number of decimal places the input number has.
7 | * @param toDecimals - The number of decimal places the output number should have.
8 | * @returns A function that converts a number from one decimal place to another.
9 | */
10 | export const ConvertDecimals = (fromDecimals: number, toDecimals: number) => {
11 | assert(fromDecimals >= 0, "requires fromDecimals as an integer >= 0");
12 | assert(toDecimals >= 0, "requires toDecimals as an integer >= 0");
13 | return (amount: BigNumberish): BigNumber => {
14 | amount = BigNumber.from(amount);
15 | if (amount.isZero()) return amount;
16 | const diff = fromDecimals - toDecimals;
17 | if (diff === 0) return amount;
18 | if (diff > 0) return amount.div(BigNumber.from("10").pow(diff));
19 | return amount.mul(BigNumber.from("10").pow(-1 * diff));
20 | };
21 | };
22 |
--------------------------------------------------------------------------------
/src/utils/ethers.ts:
--------------------------------------------------------------------------------
1 | import { Contract, ethers, providers } from "ethers";
2 | import type { Event } from "ethers";
3 | import { Provider } from "@ethersproject/providers";
4 | import { Signer } from "@ethersproject/abstract-signer";
5 |
6 | import type { TypedEvent, TypedEventFilter } from "utils/typechain";
7 |
8 | export {
9 | Provider,
10 | Signer,
11 | Contract,
12 | TypedEventFilter,
13 | TypedEvent,
14 | Event,
15 | providers,
16 | };
17 |
18 | export type Result = ethers.utils.Result;
19 |
20 | export type ContractTransaction = ethers.ContractTransaction;
21 |
22 | export interface Callable {
23 | (...args: any[]): any;
24 | }
25 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./constants";
2 | export * from "./address";
3 | export * from "./format";
4 | export * from "./bridge";
5 | export * from "./errors";
6 | export * from "./onboard";
7 | export * from "./math";
8 | export * from "./notify";
9 | export * from "./weiMath";
10 | export * from "./pool";
11 | export * from "./token";
12 | export * from "./query-keys";
13 | export * from "./ethers";
14 | export * from "./config";
15 | export * from "./providers";
16 | export * from "./rewards";
17 | export * from "./time";
18 | export * from "./amplitude";
19 | export * from "./deposits";
20 | export * from "./wait";
21 | export * from "./types";
22 | export * from "./network";
23 | export * from "./url";
24 | export * from "./sdk";
25 | export * from "./hyperliquid";
26 | export * from "./bignumber";
27 |
--------------------------------------------------------------------------------
/src/utils/localStorage.ts:
--------------------------------------------------------------------------------
1 | const TX_HISTORY_PAGE_SIZE_KEY = "txHistoryPageSize";
2 |
3 | export function setTxHistoryPageSize(value: number) {
4 | localStorage.setItem(TX_HISTORY_PAGE_SIZE_KEY, value.toString());
5 | }
6 |
7 | export function getTxHistoryPageSize(): number | undefined {
8 | return localStorage.getItem(TX_HISTORY_PAGE_SIZE_KEY)
9 | ? Number(localStorage.getItem(TX_HISTORY_PAGE_SIZE_KEY))
10 | : undefined;
11 | }
12 |
13 | export function setAccountSeenWelcomeTravellerFlow(account: string) {
14 | localStorage.setItem(`accountSeenWelcomeTravellerFlow-${account}`, account);
15 | }
16 |
17 | export function getAccountSeenWelcomeTravellerFlow(
18 | account: string
19 | ): string | null {
20 | return localStorage.getItem(`accountSeenWelcomeTravellerFlow-${account}`);
21 | }
22 |
--------------------------------------------------------------------------------
/src/utils/network.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Resolves the current vercel endpoint dynamically
3 | * @returns A valid URL of the current endpoint in vercel
4 | * @todo These are always going to be the same since we don't export REACT_APP currently.
5 | */
6 | export const resolveWebsiteUrl = () => {
7 | const env = process.env.REACT_APP_VERCEL_ENV ?? "production";
8 | switch (env) {
9 | case "production":
10 | return "https://across.to";
11 | case "preview":
12 | return `https://${process.env.REACT_APP_VERCEL_URL ?? "across.to"}`;
13 | case "development":
14 | default:
15 | return `http://localhost:3000`;
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/src/utils/notify.ts:
--------------------------------------------------------------------------------
1 | import { ContractTransaction } from "ethers";
2 | import { getProvider } from "utils";
3 |
4 | /**
5 | * Calls and waits on the Notify API to resolve the status of a TX if the chain is supported by Onboard
6 | * @param tx The transaction to wait for
7 | * @param notify The BNC Notify API that is used to handle the UI visualization
8 | * @param ignoreErrors An optional parameter to ignore tx failure and return successful
9 | **/
10 | export const waitOnTransaction = async (
11 | requiredChainId: number,
12 | tx: ContractTransaction,
13 | ignoreErrors?: boolean
14 | ): Promise => {
15 | try {
16 | const provider = getProvider(requiredChainId);
17 | await provider.waitForTransaction(tx.hash);
18 | } catch (e) {
19 | if (!ignoreErrors) {
20 | throw e;
21 | }
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/src/utils/sentry.ts:
--------------------------------------------------------------------------------
1 | import * as Sentry from "@sentry/react";
2 | import {
3 | sentryEnv,
4 | sentryDsn,
5 | isSentryEnabled,
6 | currentGitCommitHash,
7 | } from "./constants";
8 |
9 | if (isSentryEnabled) {
10 | Sentry.init({
11 | environment: sentryEnv || "development",
12 | dsn: sentryDsn,
13 | release: currentGitCommitHash,
14 | // ignore MetaMask errors we don't control
15 | ignoreErrors: [
16 | "Internal JSON-RPC error",
17 | "JsonRpcEngine",
18 | "Non-Error promise rejection captured with keys: code",
19 | ],
20 | });
21 | }
22 |
23 | export default Sentry;
24 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/index.ts:
--------------------------------------------------------------------------------
1 | import { mockServerlessAPI } from "utils/constants";
2 | import { mockedEndpoints } from "./mocked";
3 | import { prodEndpoints } from "./prod";
4 | import { ServerlessAPIEndpoints } from "./types";
5 |
6 | /**
7 | * Returns a set of functions used to interface with Across' serverless API
8 | * @returns A set of mocked or production-ready functions depending on the `REACT_APP_MOCK_SERVERLESS` env variable.
9 | */
10 | export default function getApiEndpoint(): ServerlessAPIEndpoints {
11 | if (mockServerlessAPI) {
12 | return mockedEndpoints;
13 | } else {
14 | return prodEndpoints;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/mock-adapter.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import MockAdapter from "axios-mock-adapter";
3 |
4 | const isolatedAxiosInstance = axios.create();
5 | const mockAdapter = new MockAdapter(isolatedAxiosInstance);
6 |
7 | // This sets the mock adapter on the default instance
8 | export { isolatedAxiosInstance as axios };
9 | export default mockAdapter;
10 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/mocked/bridge-limits.mocked.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber } from "ethers";
2 | import { ChainId } from "utils";
3 | import { BridgeLimitInterface } from "../types";
4 |
5 | export async function retrieveLimitsMocked(
6 | _inputToken: string | ChainId,
7 | _outputToken: string | ChainId,
8 | _fromChainId: string | ChainId,
9 | _toChainId: string | ChainId
10 | ): Promise {
11 | return {
12 | minDeposit: BigNumber.from("317845960607070"),
13 | maxDeposit: BigNumber.from("1625976243310274613043"),
14 | maxDepositInstant: BigNumber.from("148518401181482545509"),
15 | maxDepositShortDelay: BigNumber.from("1625976243310274613043"),
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/mocked/coingecko.mocked.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from "ethers";
2 |
3 | export async function coingeckoMockedApiCall(
4 | _l1Token: string,
5 | _baseCurrency: string
6 | ): Promise<{
7 | price: ethers.BigNumber;
8 | }> {
9 | return {
10 | price: ethers.utils.parseEther(String("0.17")),
11 | };
12 | }
13 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/mocked/connect-linked-wallet.mocked.ts:
--------------------------------------------------------------------------------
1 | import { providers } from "ethers";
2 |
3 | export async function connectLinkedWalletMockedCall(
4 | _backendJWT: string,
5 | _discordId: string,
6 | _signer: providers.JsonRpcSigner
7 | ): Promise {
8 | return true;
9 | }
10 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/mocked/get-deposit-stats.mocked.ts:
--------------------------------------------------------------------------------
1 | export function getDepositStatsMocked(): Promise<{
2 | totalDeposits: number;
3 | avgFillTime: number;
4 | totalVolumeUsd: number;
5 | }> {
6 | return Promise.resolve({
7 | totalDeposits: 200,
8 | avgFillTime: 200,
9 | totalVolumeUsd: 100000,
10 | });
11 | }
12 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/mocked/pools-user.mocked.ts:
--------------------------------------------------------------------------------
1 | import { PoolsUserQueryData } from "../prod/pools-user";
2 | import { parseUnits } from "utils/format";
3 | import { getConfig, hubPoolChainId } from "utils";
4 |
5 | export async function poolsUserApiCall(
6 | l1Token: string,
7 | userAddress: string
8 | ): Promise {
9 | const config = getConfig();
10 | const token = config.getTokenInfoByAddress(hubPoolChainId, l1Token);
11 | const decimals = token?.decimals ?? 18;
12 |
13 | return {
14 | address: userAddress,
15 | poolAddress: l1Token,
16 | lpTokens: parseUnits("10", decimals).toString(),
17 | positionValue: parseUnits("10", decimals).toString(),
18 | totalDeposited: parseUnits("10", decimals).toString(),
19 | feesEarned: parseUnits("0.1", decimals).toString(),
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/mocked/pools.mocked.ts:
--------------------------------------------------------------------------------
1 | import { PoolQueryData } from "../prod/pools";
2 |
3 | export async function poolsApiCall(
4 | _l1TokenOrExternalPoolToken: string,
5 | _externalPoolProvider?: string
6 | ): Promise {
7 | return {
8 | estimatedApy: "0.234",
9 | exchangeRateCurrent: "1000000000000000000",
10 | totalPoolSize: "1000000000000000000",
11 | liquidityUtilizationCurrent: "1000000000000000000",
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/mocked/retrieve-linked-wallet.mocked.ts:
--------------------------------------------------------------------------------
1 | export async function retrieveLinkedWalletMockedCall(
2 | _backendJWT: string
3 | ): Promise {
4 | return "0x815546E2E35dC9aC8A90f001cc7A859f4b21E1fd";
5 | }
6 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/mocked/retrieve-user-details.mocked.ts:
--------------------------------------------------------------------------------
1 | export async function retrieveDiscordUserDetailsMockedCall(
2 | _backendJWT: string
3 | ): Promise<{
4 | discordId: string;
5 | discordName: string;
6 | discordAvatar: string;
7 | walletLinked?: string;
8 | }> {
9 | return {
10 | discordId: "12345",
11 | discordName: "Discord User",
12 | discordAvatar: "https://picsum.photos/200",
13 | walletLinked: "0x815546E2E35dC9aC8A90f001cc7A859f4b21E1fd",
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/mocked/swap-quote.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber } from "ethers";
2 | import {
3 | SwapQuoteApiResponse,
4 | SwapQuoteApiQueryParams,
5 | } from "../prod/swap-quote";
6 |
7 | export async function swapQuoteApiCall(
8 | params: SwapQuoteApiQueryParams
9 | ): Promise {
10 | return {
11 | minExpectedInputTokenAmount: BigNumber.from(params.swapTokenAmount),
12 | routerCalldata: "0x",
13 | value: "0",
14 | swapAndBridgeAddress: "0x",
15 | dex: "1inch",
16 | slippage: 0.1,
17 | };
18 | }
19 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/prod/coingecko.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { ethers } from "ethers";
3 | import { vercelApiBaseUrl } from "utils";
4 |
5 | export type CoingeckoApiCall = typeof coingeckoApiCall;
6 |
7 | export async function coingeckoApiCall(
8 | l1Token: string,
9 | baseCurrency: string,
10 | historicalDateISO?: string
11 | ): Promise<{
12 | price: ethers.BigNumber;
13 | }> {
14 | const response = await axios.get(`${vercelApiBaseUrl}/api/coingecko`, {
15 | params: {
16 | l1Token,
17 | baseCurrency,
18 | date: historicalDateISO,
19 | },
20 | });
21 | const result = response.data;
22 | const price =
23 | baseCurrency === "usd"
24 | ? ethers.utils.parseEther(String(result.price))
25 | : ethers.BigNumber.from(result.price);
26 | return {
27 | price,
28 | };
29 | }
30 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/prod/get-deposit-stats.prod.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { rewardsApiUrl } from "utils/constants";
3 |
4 | export async function getDepositStats(): Promise<{
5 | totalDeposits: number;
6 | avgFillTime: number;
7 | totalVolumeUsd: number;
8 | }> {
9 | const axiosResponse = await axios.get<{
10 | totalDeposits: number;
11 | avgFillTime: number;
12 | totalVolumeUsd: number;
13 | }>(`${rewardsApiUrl}/deposits/stats`);
14 | return axiosResponse.data;
15 | }
16 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/prod/pools-user.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { vercelApiBaseUrl } from "utils";
3 |
4 | export type PoolsUserApiCall = typeof poolsUserApiCall;
5 |
6 | export type PoolsUserQueryData = {
7 | address: string;
8 | poolAddress: string;
9 | lpTokens: string;
10 | positionValue: string;
11 | totalDeposited: string;
12 | feesEarned: string;
13 | };
14 |
15 | export async function poolsUserApiCall(
16 | l1Token: string,
17 | userAddress: string
18 | ): Promise {
19 | const response = await axios.get(`${vercelApiBaseUrl}/api/pools-user`, {
20 | params: {
21 | token: l1Token,
22 | user: userAddress,
23 | },
24 | });
25 | return response.data;
26 | }
27 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/prod/pools.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { vercelApiBaseUrl } from "utils";
3 |
4 | export type PoolsApiCall = typeof poolsApiCall;
5 |
6 | export type PoolQueryData = {
7 | estimatedApy: string;
8 | exchangeRateCurrent: string;
9 | totalPoolSize: string;
10 | liquidityUtilizationCurrent: string;
11 | };
12 |
13 | export async function poolsApiCall(
14 | l1TokenOrExternalPoolToken: string,
15 | externalPoolProvider?: string
16 | ): Promise {
17 | const response = await axios.get(`${vercelApiBaseUrl}/api/pools`, {
18 | params: {
19 | token: l1TokenOrExternalPoolToken,
20 | externalPoolProvider,
21 | },
22 | });
23 | return response.data;
24 | }
25 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/prod/retrieve-discord-user-details.prod.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { rewardsApiUrl } from "utils/constants";
3 | import getApiEndpoint from "..";
4 |
5 | export async function retrieveDiscordUserDetails(backendJWT: string): Promise<{
6 | discordId: string;
7 | discordName: string;
8 | discordAvatar: string;
9 | walletLinked?: string;
10 | }> {
11 | // Call to scraper API and resolve the JWT
12 | const jwtResolver = await axios.get<{
13 | user: {
14 | discordId: string;
15 | discordName: string;
16 | discordAvatar: string;
17 | };
18 | }>(`${rewardsApiUrl}/users/me`, {
19 | headers: {
20 | Authorization: `Bearer ${backendJWT}`,
21 | },
22 | });
23 | const walletLinked =
24 | await getApiEndpoint().prelaunch.linkedWallet(backendJWT);
25 | return {
26 | discordId: jwtResolver.data.user.discordId,
27 | discordName: jwtResolver.data.user.discordName,
28 | discordAvatar: jwtResolver.data.user.discordAvatar,
29 | walletLinked,
30 | };
31 | }
32 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/prod/retrieve-linked-wallet.prod.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosError } from "axios";
2 | import { rewardsApiUrl } from "utils/constants";
3 |
4 | export async function retrieveLinkedWallet(
5 | backendJWT: string
6 | ): Promise {
7 | try {
8 | // Resolve whether the user has a wallet
9 | const response = await axios.get<{ walletAddress: string }>(
10 | `${rewardsApiUrl}/users/me/wallets`,
11 | {
12 | headers: {
13 | Authorization: `Bearer ${backendJWT}`,
14 | },
15 | }
16 | );
17 | return response.data.walletAddress;
18 | } catch (e: unknown) {
19 | // Test for the case where this function returns a 404
20 | // and if this is not that error, then propagate `e`
21 | if (!(e instanceof AxiosError) || e?.response?.status !== 404) {
22 | throw e;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/prod/retrieveLimits.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { ChainId, vercelApiBaseUrl } from "utils";
3 | import { BridgeLimitInterface } from "../types";
4 |
5 | export async function retrieveLimits(
6 | inputToken: string,
7 | outputToken: string,
8 | fromChainId: string | ChainId,
9 | toChainId: string | ChainId
10 | ): Promise {
11 | const { data } = await axios.get(
12 | `${vercelApiBaseUrl}/api/limits`,
13 | {
14 | params: {
15 | inputToken,
16 | outputToken,
17 | originChainId: fromChainId,
18 | destinationChainId: toChainId,
19 | allowUnmatchedDecimals: true,
20 | },
21 | }
22 | );
23 | return data;
24 | }
25 |
--------------------------------------------------------------------------------
/src/utils/serverless-api/prod/rewards.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { RewardsApiInterface } from "../types";
3 | import { rewardsApiUrl } from "utils";
4 |
5 | export default async function rewardsApiCall(
6 | address: string,
7 | jwt?: string
8 | ): Promise {
9 | try {
10 | const response = await axios.get(`${rewardsApiUrl}/airdrop/rewards`, {
11 | headers: {
12 | Authorization: `Bearer ${jwt}`,
13 | },
14 | params: {
15 | address,
16 | },
17 | });
18 | if (response.data) {
19 | return response.data;
20 | } else {
21 | return null;
22 | }
23 | } catch (err) {
24 | console.error(err);
25 | return null;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/utils/ternary.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Creates a reusable ternary operation function.
3 | * @param expression The logical expression to test the ternary. For example, the variable A in -> A ? B : C
4 | * @param fallbackValue The Else value in a ternary. For example, the variable C in -> A ? B : C
5 | * @returns A function that emulates a ternary operation. This closure takes the truthy return and evaluates the ternary.
6 | */
7 | export function repeatableTernaryBuilder(
8 | expression: boolean,
9 | fallbackValue: Type
10 | ) {
11 | /**
12 | * Represents a ternary operation.
13 | * @param value The returned in a ternary if the expression is true. For example, the variable B in -> A ? B : C
14 | * @returns `value` if `expression` is true and `value` is defined, else `fallbackValue`
15 | */
16 | function closure(value?: Type): Type {
17 | if (expression && value) {
18 | return value;
19 | } else {
20 | return fallbackValue;
21 | }
22 | }
23 | return closure;
24 | }
25 |
--------------------------------------------------------------------------------
/src/utils/tests/rewards.test.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber } from "ethers";
2 | import { parseEtherLike } from "utils/format";
3 |
4 | import { getBaseRewardsApr } from "../rewards";
5 |
6 | // Enums break ts-jest
7 | // https://github.com/kulshekhar/ts-jest/issues/3397
8 | jest.mock("../providers.ts", () => ({
9 | ChainId: {
10 | MAINNET: 1,
11 | },
12 | }));
13 |
14 | describe("#getBaseRewardsApr", () => {
15 | const totalBaseRewardsPerYear = BigNumber.from(1);
16 |
17 | test("return base rewards APR of 100%", () => {
18 | const totalStaked = totalBaseRewardsPerYear;
19 | expect(
20 | getBaseRewardsApr(totalBaseRewardsPerYear, totalStaked)
21 | ).toMatchObject(parseEtherLike("1"));
22 | });
23 |
24 | test("return base rewards APR of 50%", () => {
25 | const totalStaked = totalBaseRewardsPerYear.mul(2);
26 | expect(
27 | getBaseRewardsApr(totalBaseRewardsPerYear, totalStaked)
28 | ).toMatchObject(parseEtherLike("0.5"));
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/src/utils/tests/weiMath.test.ts:
--------------------------------------------------------------------------------
1 | import { toWeiSafe } from "../weiMath";
2 |
3 | describe("toWeiSafe", () => {
4 | it("Converts the value without an error", () => {
5 | const toBigNum = toWeiSafe("1").toString();
6 | expect(toBigNum).toEqual("1000000000000000000");
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/src/utils/types.ts:
--------------------------------------------------------------------------------
1 | import { Theme } from "@emotion/react";
2 | import { StyledComponent } from "@emotion/styled";
3 |
4 | export type StylizedSVG = StyledComponent<
5 | React.SVGProps & {
6 | title?: string | undefined;
7 | } & {
8 | children?: React.ReactNode;
9 | } & {
10 | theme?: Theme | undefined;
11 | }
12 | >;
13 |
14 | export type VoidHandler = () => void;
15 |
16 | export type DepositStatusFilter = "all" | "pending" | "filled" | "refunded";
17 |
--------------------------------------------------------------------------------
/src/utils/url.ts:
--------------------------------------------------------------------------------
1 | export function getBridgeUrlWithQueryParams({
2 | fromChainId,
3 | toChainId,
4 | inputTokenSymbol,
5 | outputTokenSymbol,
6 | externalProjectId,
7 | }: {
8 | fromChainId: number;
9 | toChainId: number;
10 | inputTokenSymbol: string;
11 | outputTokenSymbol?: string;
12 | externalProjectId?: string;
13 | }) {
14 | const cleanParams = Object.entries({
15 | from: fromChainId.toString(),
16 | to: toChainId.toString(),
17 | inputToken: inputTokenSymbol,
18 | outputToken: outputTokenSymbol,
19 | externalProjectId,
20 | }).reduce((acc, [key, value]) => {
21 | if (value) {
22 | return { ...acc, [key]: value };
23 | }
24 | return acc;
25 | }, {});
26 | return "/bridge?" + new URLSearchParams(cleanParams).toString();
27 | }
28 |
--------------------------------------------------------------------------------
/src/utils/wait.ts:
--------------------------------------------------------------------------------
1 | export function wait(ms: number) {
2 | return new Promise((resolve) => setTimeout(resolve, ms));
3 | }
4 |
--------------------------------------------------------------------------------
/src/views/Bridge/Bridge.styles.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { QUERIESV2 } from "utils";
3 |
4 | export const Wrapper = styled.div`
5 | background-color: transparent;
6 |
7 | width: 100%;
8 |
9 | margin: 48px auto 20px;
10 | display: flex;
11 | flex-direction: column;
12 | gap: 24px;
13 |
14 | @media ${QUERIESV2.sm.andDown} {
15 | margin: 16px auto;
16 | gap: 16px;
17 | }
18 | `;
19 |
--------------------------------------------------------------------------------
/src/views/Bridge/components/Breadcrumb.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import BreadcrumbV2 from "components/BreadcrumbV2";
3 | import { Text } from "components/Text";
4 |
5 | const Breadcrumb = () => (
6 |
9 | Bridge
10 |
11 | }
12 | />
13 | );
14 |
15 | export default Breadcrumb;
16 |
17 | const Wrapper = styled.div`
18 | display: flex;
19 | flex-direction: row;
20 | align-items: center;
21 | padding: 0px;
22 | gap: 12px;
23 | `;
24 |
--------------------------------------------------------------------------------
/src/views/Bridge/components/RouteNotSupportedTooltipText.tsx:
--------------------------------------------------------------------------------
1 | import { Text } from "components/Text";
2 | import { getChainInfo } from "utils";
3 |
4 | type Props = {
5 | symbol: string;
6 | fromChain: number;
7 | toChain: number;
8 | };
9 |
10 | export const RouteNotSupportedTooltipText = ({
11 | symbol,
12 | fromChain,
13 | toChain,
14 | }: Props) => {
15 | return (
16 |
17 |
18 | {symbol}
19 | {" "}
20 | is not supported on route{" "}
21 |
22 | {getChainInfo(fromChain).name}
23 | {" "}
24 | {"->"}{" "}
25 |
26 | {getChainInfo(toChain).name}
27 |
28 | . Pick a different asset or change the route.
29 |
30 | );
31 | };
32 |
33 | export default RouteNotSupportedTooltipText;
34 |
--------------------------------------------------------------------------------
/src/views/Bridge/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./Bridge";
2 |
--------------------------------------------------------------------------------
/src/views/DepositStatus/components/Breadcrumb.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 |
3 | import BreadcrumbV2 from "components/BreadcrumbV2";
4 | import { Text } from "components/Text";
5 | import { shortenString } from "utils";
6 |
7 | type Props = {
8 | depositTxHash: string;
9 | };
10 |
11 | export function Breadcrumb({ depositTxHash }: Props) {
12 | return (
13 |
16 | {shortenString(depositTxHash, "..", 4)}
17 |
18 | }
19 | />
20 | );
21 | }
22 |
23 | const Wrapper = styled.div`
24 | display: flex;
25 | flex-direction: row;
26 | align-items: center;
27 | padding: 0px;
28 | gap: 12px;
29 | `;
30 |
--------------------------------------------------------------------------------
/src/views/DepositStatus/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from "./DepositStatus";
2 |
--------------------------------------------------------------------------------
/src/views/DepositStatus/types.ts:
--------------------------------------------------------------------------------
1 | export type DepositStatus =
2 | | "deposit-reverted"
3 | | "depositing"
4 | | "filling"
5 | | "filled";
6 |
--------------------------------------------------------------------------------
/src/views/LiquidityPool/LiquidityPool.styles.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { PrimaryButton } from "components/Button";
3 | import { QUERIESV2 } from "utils";
4 |
5 | export const Container = styled.div`
6 | background-color: transparent;
7 |
8 | width: 100%;
9 |
10 | margin: 48px auto 20px;
11 | display: flex;
12 | flex-direction: column;
13 | gap: 24px;
14 |
15 | @media ${QUERIESV2.sm.andDown} {
16 | margin: 16px auto;
17 | gap: 16px;
18 | }
19 | `;
20 |
21 | export const StatsRow = styled.div`
22 | display: flex;
23 | flex-direction: row;
24 | width: 100%;
25 | gap: 24px;
26 |
27 | @media ${QUERIESV2.sm.andDown} {
28 | flex-direction: column;
29 | }
30 | `;
31 |
32 | export const Divider = styled.div`
33 | width: 100%;
34 | background: #3e4047;
35 | height: 1px;
36 | `;
37 |
38 | export const Button = styled(PrimaryButton)`
39 | width: 100%;
40 | `;
41 |
--------------------------------------------------------------------------------
/src/views/LiquidityPool/components/Breadcrumb.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import BreadcrumbV2 from "components/BreadcrumbV2";
3 | import { Text } from "components/Text";
4 |
5 | export function Breadcrumb() {
6 | return (
7 |
10 | Pool
11 |
12 | }
13 | />
14 | );
15 | }
16 |
17 | export default Breadcrumb;
18 |
19 | const Wrapper = styled.div`
20 | display: flex;
21 | flex-direction: row;
22 | align-items: center;
23 | padding: 0px;
24 | gap: 12px;
25 | `;
26 |
--------------------------------------------------------------------------------
/src/views/LiquidityPool/components/StatBox.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 |
3 | import { Text } from "components/Text";
4 |
5 | type Props = {
6 | label: string;
7 | value: string;
8 | };
9 |
10 | export default function StatBox({ label, value }: Props) {
11 | return (
12 |
13 | {label}
14 | {value}
15 |
16 | );
17 | }
18 |
19 | const Container = styled.div`
20 | display: flex;
21 | flex-direction: column;
22 | align-items: flex-start;
23 | flex: 1;
24 | padding: 16px;
25 | gap: 4px;
26 |
27 | border: 1px solid #3e4047;
28 | border-radius: 16px;
29 | `;
30 |
--------------------------------------------------------------------------------
/src/views/LiquidityPool/components/UserStatRow.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 |
3 | import { Text } from "components/Text";
4 |
5 | type Props = {
6 | label: string;
7 | value: string;
8 | tokenLogoURI?: string;
9 | };
10 |
11 | export default function UserStatBox({ label, value, tokenLogoURI }: Props) {
12 | return (
13 |
14 | {label}
15 |
16 | {value}
17 | {tokenLogoURI &&
}
18 |
19 |
20 | );
21 | }
22 |
23 | const Container = styled.div`
24 | display: flex;
25 | width: 100%;
26 | flex-direction: row;
27 | justify-content: space-between;
28 | align-items: center;
29 | `;
30 |
31 | const ValueContainer = styled.span`
32 | display: flex;
33 | flex-direction: row;
34 | align-items: center;
35 | gap: 4px;
36 | img {
37 | height: 16px;
38 | width: 16px;
39 | }
40 | `;
41 |
--------------------------------------------------------------------------------
/src/views/LiquidityPool/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./LiquidityPool";
2 |
--------------------------------------------------------------------------------
/src/views/LiquidityPool/utils.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber, utils } from "ethers";
2 |
3 | export function parseAndValidateAmountInput(
4 | amountInput: string,
5 | amountDecimals: number,
6 | maxAmount: BigNumber
7 | ) {
8 | const cleanedInput = amountInput.replaceAll(",", "");
9 |
10 | if (cleanedInput.split(".")[1]?.length > amountDecimals) {
11 | throw new Error("Input amount decimals exceeds max decimals");
12 | }
13 |
14 | let amountBN: BigNumber;
15 | try {
16 | amountBN = utils.parseUnits(cleanedInput, amountDecimals);
17 | } catch (error) {
18 | throw new Error("Invalid amount");
19 | }
20 |
21 | if (maxAmount.lt(amountBN)) {
22 | throw new Error("Input amount exceeds max amount");
23 | }
24 |
25 | if (amountBN.lte(0)) {
26 | throw new Error("Input amount must be greater than 0");
27 | }
28 |
29 | return amountBN;
30 | }
31 |
--------------------------------------------------------------------------------
/src/views/NotFound/NotFound.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Wrapper,
3 | Title,
4 | Body,
5 | Link,
6 | CloudWrapper,
7 | StyledEmptyCloud,
8 | Content,
9 | } from "./NotFound.styles";
10 | import Footer from "components/Footer";
11 |
12 | interface Props {
13 | custom404Message?: string;
14 | }
15 | const NotFound: React.FC = ({ custom404Message }) => {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 | 404
23 | {custom404Message || "Page not found"}
24 | Go back to Across
25 |
26 |
27 |
28 | );
29 | };
30 |
31 | export default NotFound;
32 |
--------------------------------------------------------------------------------
/src/views/NotFound/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./NotFound";
2 |
--------------------------------------------------------------------------------
/src/views/Rewards/components/GenericStakingPoolTable/GenericStakingPoolTable.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import RewardTable from "components/RewardTable";
3 | import { StakingPool } from "utils/staking-pool";
4 | import { formatRow, headers } from "./GenericStakingPoolFormatter";
5 |
6 | type GenericStakingPoolTableType = {
7 | poolData?: StakingPool[];
8 | isLoading?: boolean;
9 | greyscaleTokenLogo?: boolean;
10 | };
11 |
12 | const GenericStakingPoolTable = ({
13 | poolData = [],
14 | isLoading,
15 | greyscaleTokenLogo = false,
16 | }: GenericStakingPoolTableType) => {
17 | const rows = poolData.map((datum) => formatRow(datum, greyscaleTokenLogo));
18 | return (
19 |
20 |
27 |
28 | );
29 | };
30 |
31 | export default GenericStakingPoolTable;
32 |
33 | const Wrapper = styled.div`
34 | width: 100%;
35 | `;
36 |
--------------------------------------------------------------------------------
/src/views/Rewards/components/GenericStakingPoolTable/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./GenericStakingPoolTable";
2 |
--------------------------------------------------------------------------------
/src/views/Rewards/hooks/useRewardProgramCard.ts:
--------------------------------------------------------------------------------
1 | import { useConnection, useRewardSummary } from "hooks";
2 | import { getToken, rewardProgramTypes, rewardPrograms } from "utils";
3 | import { BigNumber } from "ethers";
4 |
5 | export function useRewardProgramCard(programName: rewardProgramTypes) {
6 | const { account } = useConnection();
7 | const { summary } = useRewardSummary(programName, account);
8 | const programDetail = rewardPrograms[programName];
9 | const token = getToken(programDetail.rewardTokenSymbol);
10 | const rewardsAmount = summary.unclaimedRewards ?? 0;
11 | return {
12 | ...programDetail,
13 | token,
14 | rewardsAmount: BigNumber.from(rewardsAmount),
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/src/views/Rewards/hooks/useStakingPools.tsx:
--------------------------------------------------------------------------------
1 | import { useAllStakingPools } from "hooks";
2 |
3 | export const useStakingPools = () => {
4 | const allStakingPoolsQueries = useAllStakingPools();
5 | const isLoading = allStakingPoolsQueries.some((query) => query.isLoading);
6 | const stakingPools = allStakingPoolsQueries.flatMap(
7 | (query) => query.data || []
8 | );
9 | const enabledPools = stakingPools.filter((pool) => pool.poolEnabled);
10 | const poolsWithLP = enabledPools.filter((pool) => pool.isStakingPoolOfUser);
11 | const myPools = poolsWithLP.filter((pool) =>
12 | pool.userAmountOfLPStaked.gte(0)
13 | );
14 | const allPools = enabledPools.filter((pool) => !pool.isStakingPoolOfUser);
15 |
16 | return {
17 | myPools,
18 | allPools,
19 | enabledPools,
20 | isLoading,
21 | };
22 | };
23 |
--------------------------------------------------------------------------------
/src/views/Rewards/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./Rewards";
2 |
--------------------------------------------------------------------------------
/src/views/RewardsProgram/ARBRebatesProgram.tsx:
--------------------------------------------------------------------------------
1 | import GenericRewardsProgram from "./GenericRewardsProgram/GenericRewardsProgram";
2 | import { useARBRebatesProgram } from "./hooks/useARBRebatesProgram";
3 |
4 | const OPRebatesProgram = () => {
5 | const { labels, rewardsAmount, claimableAmount } = useARBRebatesProgram();
6 | return (
7 |
16 | );
17 | };
18 |
19 | export default OPRebatesProgram;
20 |
--------------------------------------------------------------------------------
/src/views/RewardsProgram/OPRebatesProgram.tsx:
--------------------------------------------------------------------------------
1 | import GenericRewardsProgram from "./GenericRewardsProgram/GenericRewardsProgram";
2 | import { useOPRebatesProgram } from "./hooks/useOPRebatesProgram";
3 |
4 | const OPRebatesProgram = () => {
5 | const { labels, rewardsAmount, claimableAmount } = useOPRebatesProgram();
6 | return (
7 |
16 | );
17 | };
18 |
19 | export default OPRebatesProgram;
20 |
--------------------------------------------------------------------------------
/src/views/Staking/Staking.styles.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { QUERIESV2 } from "utils";
3 |
4 | export const Wrapper = styled.div`
5 | background-color: transparent;
6 |
7 | width: 100%;
8 |
9 | margin: 48px auto 20px;
10 | display: flex;
11 | flex-direction: column;
12 | gap: 32px;
13 |
14 | @media ${QUERIESV2.sm.andDown} {
15 | margin: 16px auto;
16 | gap: 16px;
17 | }
18 | `;
19 |
20 | export const Divider = styled.div`
21 | width: 100%;
22 | height: 1px;
23 |
24 | background: #3e4047;
25 |
26 | flex: none;
27 | order: 0;
28 | align-self: stretch;
29 | flex-grow: 0;
30 | `;
31 |
--------------------------------------------------------------------------------
/src/views/Staking/components/StakingExitAction/StakingExitAction.styles.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { QUERIESV2 } from "utils";
3 |
4 | export const IconPairContainer = styled.div`
5 | padding-right: 4px;
6 | `;
7 |
8 | export const Logo = styled.img`
9 | height: 24px;
10 | width: 24px;
11 |
12 | @media ${QUERIESV2.sm.andDown} {
13 | height: 24px;
14 | width: 24px;
15 | }
16 | `;
17 |
18 | export const Text = styled.span`
19 | font-weight: 400;
20 | font-size: 18px;
21 | line-height: 26px;
22 | color: #e0f3ff;
23 | `;
24 |
25 | export const TitleLogo = styled.div<{ extendWidth: boolean }>`
26 | display: flex;
27 | flex-direction: row;
28 | align-items: center;
29 | gap: ${({ extendWidth }) => (extendWidth ? 18 : 12)}px;
30 |
31 | @media ${QUERIESV2.sm.andDown} {
32 | gap: 8px;
33 | }
34 | `;
35 |
--------------------------------------------------------------------------------
/src/views/Staking/components/StakingExitAction/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from "./StakingExitAction";
2 |
--------------------------------------------------------------------------------
/src/views/Staking/components/StakingForm/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./StakingForm";
2 |
--------------------------------------------------------------------------------
/src/views/Staking/components/StakingInputBlock/StakingInputBlock.styles.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { PrimaryButton } from "components/Button";
3 |
4 | export const Wrapper = styled.div`
5 | display: flex;
6 | flex-direction: column;
7 | gap: 12px;
8 | `;
9 |
10 | export const ButtonWrapper = styled.div`
11 | width: 100%;
12 | `;
13 |
14 | export const StakeButton = styled(PrimaryButton)`
15 | text-transform: capitalize;
16 | width: 100%;
17 | `;
18 |
19 | export const StakeButtonContentWrapper = styled.div`
20 | display: flex;
21 | gap: 6px;
22 | justify-content: center;
23 | flex-direction: row;
24 | `;
25 |
26 | export const IconPairContainer = styled.div`
27 | padding-top: 8px;
28 | margin-right: 8px;
29 | `;
30 |
--------------------------------------------------------------------------------
/src/views/Staking/components/StakingInputBlock/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./StakingInputBlock";
2 |
--------------------------------------------------------------------------------
/src/views/Staking/components/StakingReward/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./StakingReward";
2 |
--------------------------------------------------------------------------------
/src/views/Staking/components/index.ts:
--------------------------------------------------------------------------------
1 | export { default as StakingExitAction } from "./StakingExitAction";
2 | export { default as StakingForm } from "./StakingForm";
3 | export { default as StakingReward } from "./StakingReward";
4 |
--------------------------------------------------------------------------------
/src/views/Staking/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./Staking";
2 |
--------------------------------------------------------------------------------
/src/views/Staking/types.ts:
--------------------------------------------------------------------------------
1 | import { StakingPool } from "utils/staking-pool";
2 | import { StakingActionFunctionType } from "./hooks/useStakingAction";
3 |
4 | type GenericStakingComponentProps = {
5 | isConnected: boolean;
6 | poolData: StakingPool;
7 | isWrongNetwork: boolean;
8 | switchNetwork: () => Promise;
9 | };
10 |
11 | export type StakingRewardPropType = GenericStakingComponentProps & {
12 | claimActionHandler: () => Promise;
13 | isMutating: boolean;
14 | };
15 |
16 | export type StakingPoolTokenPairLogoURIs = [string, string];
17 |
18 | export type StakingFormPropType = GenericStakingComponentProps & {
19 | logoURI: string;
20 | logoURIs?: StakingPoolTokenPairLogoURIs;
21 | stakeActionFn: StakingActionFunctionType;
22 | unstakeActionFn: StakingActionFunctionType;
23 | isDataLoading: boolean;
24 | isMutating: boolean;
25 | tokenSymbol: string;
26 | };
27 |
--------------------------------------------------------------------------------
/src/views/Transactions/components/EmptyTable.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 |
3 | import BgUrl from "assets/bg-banners/empty-deposits-banner.svg";
4 |
5 | type Props = {
6 | children?: React.ReactNode;
7 | };
8 |
9 | export function EmptyTable({ children }: Props) {
10 | return {children};
11 | }
12 |
13 | const Wrapper = styled.div`
14 | height: 200px;
15 | display: flex;
16 | justify-content: center;
17 | align-items: center;
18 | flex-direction: column;
19 | margin-top: -24px;
20 | gap: 24px;
21 |
22 | background-image: url(${BgUrl});
23 | `;
24 |
--------------------------------------------------------------------------------
/src/views/Transactions/components/SpeedUpModal/index.tsx:
--------------------------------------------------------------------------------
1 | export { SpeedUpModal } from "./SpeedUpModal";
2 |
--------------------------------------------------------------------------------
/src/views/Transactions/hooks/usePagination.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | import { getTxHistoryPageSize, setTxHistoryPageSize } from "utils/localStorage";
4 |
5 | const DEFAULT_TX_HISTORY_PAGE_SIZE = 10;
6 |
7 | export function useCurrentPage() {
8 | const [currentPage, setCurrentPage] = useState(0);
9 |
10 | return {
11 | currentPage,
12 | setCurrentPage,
13 | };
14 | }
15 |
16 | export function usePageSize() {
17 | const [pageSize, setPageSize] = useState(
18 | () => getTxHistoryPageSize() || DEFAULT_TX_HISTORY_PAGE_SIZE
19 | );
20 |
21 | const handlePageSizeChange = (newPageSize: number) => {
22 | setPageSize(newPageSize);
23 | setTxHistoryPageSize(newPageSize);
24 | };
25 |
26 | return { pageSize, handlePageSizeChange };
27 | }
28 |
--------------------------------------------------------------------------------
/src/views/Transactions/index.ts:
--------------------------------------------------------------------------------
1 | export { Transactions as default } from "./Transactions";
2 |
--------------------------------------------------------------------------------
/src/views/Transactions/types.ts:
--------------------------------------------------------------------------------
1 | import { Deposit } from "hooks/useDeposits";
2 | import { Token } from "utils";
3 |
4 | export type { DepositStatusFilter } from "utils";
5 |
6 | export interface TxLink {
7 | text: string;
8 | url: string;
9 | }
10 |
11 | export type SupportedTxTuple = [token: Token, tx: Deposit];
12 |
--------------------------------------------------------------------------------
/src/views/index.ts:
--------------------------------------------------------------------------------
1 | export { default as LiquidityPool } from "./LiquidityPool";
2 | export { default as Transactions } from "./Transactions";
3 | export { default as Rewards } from "./Rewards";
4 | export { default as NotFound } from "./NotFound";
5 | export { default as Staking } from "./Staking";
6 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "module": "commonjs",
6 | "moduleResolution": "node",
7 | "sourceMap": false,
8 | "esModuleInterop": true,
9 | "outDir": "./e2e/tests-out",
10 | "skipLibCheck": true,
11 | "baseUrl": "e2e"
12 | },
13 | "paths": {
14 | "*": ["./e2e/*"]
15 | },
16 | "include": ["e2e"]
17 | }
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2020",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "noFallthroughCasesInSwitch": true,
12 | "module": "CommonJS",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx",
18 | "baseUrl": "src"
19 | },
20 | "paths": {
21 | "*": ["./src/*"]
22 | },
23 | "include": ["src", "test", "api"]
24 | }
25 |
--------------------------------------------------------------------------------