├── .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 | 2 | 3 | 4 | 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 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /scripts/chain-configs/base/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /scripts/chain-configs/blast/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /scripts/chain-configs/bsc/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 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 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /scripts/chain-configs/linea/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 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 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /scripts/chain-configs/mainnet/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 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 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /scripts/chain-configs/mode/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | 2 | 5 | 8 | -------------------------------------------------------------------------------- /scripts/chain-configs/soneium/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | -------------------------------------------------------------------------------- /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 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /scripts/chain-configs/tatara/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /scripts/chain-configs/unichain-sepolia/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 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 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /scripts/chain-configs/unichain/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 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 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /scripts/chain-configs/zk-sync/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 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 | 2 | 5 | -------------------------------------------------------------------------------- /scripts/extern-configs/hyperliquid/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /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 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/chain-logos/base-sepolia-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/chain-logos/base-sepolia.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/chain-logos/base.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/chain-logos/blast-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/chain-logos/blast-sepolia-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/chain-logos/blast-sepolia.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/chain-logos/blast.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/chain-logos/bsc-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/chain-logos/bsc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/chain-logos/doctor-who-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/chain-logos/doctor-who.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/chain-logos/linea-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/chain-logos/linea.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/chain-logos/mainnet-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/chain-logos/mainnet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/chain-logos/mode-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/chain-logos/mode-sepolia-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/chain-logos/mode-sepolia.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/chain-logos/mode.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/chain-logos/sepolia-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/chain-logos/sepolia.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/chain-logos/soneium-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | -------------------------------------------------------------------------------- /src/assets/chain-logos/soneium.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | -------------------------------------------------------------------------------- /src/assets/chain-logos/tatara-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/chain-logos/tatara.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/chain-logos/unichain-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/chain-logos/unichain-sepolia-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/chain-logos/unichain-sepolia.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/chain-logos/unichain.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/chain-logos/zk-sync-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/assets/chain-logos/zk-sync.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/assets/extern-logos/hyperliquid-grayscale.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /src/assets/extern-logos/hyperliquid.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/arrow-up-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/assets/icons/arrow-up-right-boxed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/arrow-up-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/checkmark-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/chevron-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/chevron-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/icons/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/icons/connectors.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/hamburger.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/plus-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/question-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/referree.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/icons/refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/icons/wallet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/x-grey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/icons/x-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/zap.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/token-logos/acx.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/token-logos/bal.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 10 | 12 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/assets/token-logos/bnb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/token-logos/eth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/token-logos/usdb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/token-logos/usdt.svg: -------------------------------------------------------------------------------- 1 | tether-usdt-logo -------------------------------------------------------------------------------- /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 = () =>