├── .commitlintrc.json
├── .env.development
├── .env.production
├── .env.test
├── .eslintrc
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── bug-report.md
│ ├── config.yml
│ ├── feature-request.md
│ ├── listing-request.md
│ └── something-else.md
├── pull_request_template.md
└── workflows
│ ├── lint.yml
│ └── tests.yml
├── .gitignore
├── .prettierrc
├── .yarnrc
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── cypress.json
├── cypress
├── integration
│ ├── add-liquidity.test.ts
│ ├── landing.test.ts
│ ├── pool.test.ts
│ ├── remove-liquidity.test.ts
│ ├── send.test.ts
│ └── swap.test.ts
├── support
│ ├── commands.d.ts
│ ├── commands.js
│ └── index.js
└── tsconfig.json
├── package.json
├── public
├── favicon.png
├── images
│ ├── 192x192_App_Icon.png
│ ├── 512x512_App_Icon.png
│ ├── arch-dark.svg
│ ├── arch-light.svg
│ ├── blue-loader.svg
│ ├── coins
│ │ ├── 0x007ea5c0ea75a8df45d288a4debdd5bb633f9e56.png
│ │ ├── 0x039cb485212f996a9dbb85a9a75d898f94d38da6.png
│ │ ├── 0x03ff0ff224f904be3118461335064bb48df47938.png
│ │ ├── 0x04c747b40be4d535fc83d09939fb0f626f32800b.png
│ │ ├── 0x0d8ce2a99bb6e3b7db580ed848240e4a0f9ae153.png
│ │ ├── 0x0d9319565be7f53CeFE84Ad201Be3f40feAE2740.png
│ │ ├── 0x0da6ed8b13214ff28e9ca979dd37439e8a88f6c4.png
│ │ ├── 0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82.png
│ │ ├── 0x0eb3a705fc54725037cc9e008bdede697f62f335.png
│ │ ├── 0x0f9e4d49f25de22c2202af916b681fbb3790497b.png
│ │ ├── 0x101d82428437127bf1608f699cd651e6abf9766e.png
│ │ ├── 0x1203355742e76875154c0d13eb81dcd7711dc7d9.png
│ │ ├── 0x1613957159e9b0ac6c80e824f7eea748a32a0ae2.png
│ │ ├── 0x16939ef78684453bfdfb47825f8a5f714f12623a.png
│ │ ├── 0x190b589cf9Fb8DDEabBFeae36a813FFb2A702454.png
│ │ ├── 0x1A2fb0Af670D0234c2857FaD35b789F8Cb725584.png
│ │ ├── 0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3.png
│ │ ├── 0x1ba42e5193dfa8b03d15dd1b86a3113bbbef8eeb.png
│ │ ├── 0x1d1eb8e8293222e1a29d2c0e4ce6c0acfd89aaac.png
│ │ ├── 0x1d2f0da169ceb9fc7b3144628db156f3f6c60dbe.png
│ │ ├── 0x1f7216fdb338247512ec99715587bb97bbf96eae.png
│ │ ├── 0x1ffd0b47127fdd4097e54521c9e2c7f0d66aafc5.png
│ │ ├── 0x2090c8295769791ab7A3CF1CC6e0AA19F35e441A.png
│ │ ├── 0x211ffbe424b90e25a15531ca322adf1559779e45.png
│ │ ├── 0x2170ed0880ac9a755fd29b2688956bd959f933f8.png
│ │ ├── 0x2222227e22102fe3322098e4cbfe18cfebd57c95.png
│ │ ├── 0x23396cF899Ca06c4472205fC903bDB4de249D6fC.png
│ │ ├── 0x233d91A0713155003fc4DcE0AFa871b508B3B715.png
│ │ ├── 0x23e8a70534308a4AAF76fb8C32ec13d17a3BD89e.png
│ │ ├── 0x250632378e573c6be1ac2f97fcdf00515d0aa91b.png
│ │ ├── 0x250b211EE44459dAd5Cd3bCa803dD6a7EcB5d46C.png
│ │ ├── 0x25e9d05365c867e59c1904e7463af9f312296f9e.png
│ │ ├── 0x26a5dfab467d4f58fb266648cae769503cec9580.png
│ │ ├── 0x2cD1075682b0FCCaADd0Ca629e138E64015Ba11c.png
│ │ ├── 0x37dfACfaeDA801437Ff648A1559d73f4C40aAcb7.png
│ │ ├── 0x393B312C01048b3ed2720bF1B090084C09e408A1.png
│ │ ├── 0x3947B992DC0147D2D89dF0392213781b04B25075.png
│ │ ├── 0x3ee2200efb3400fabb9aacf31297cbdd1d435d47.png
│ │ ├── 0x3fda9383a84c05ec8f7630fe10adf1fac13241cc.png
│ │ ├── 0x4131b87f74415190425ccd873048c708f8005823.png
│ │ ├── 0x42712dF5009c20fee340B245b510c0395896cF6e.png
│ │ ├── 0x42f6f551ae042cbe50c739158b4f0cac0edb9096.png
│ │ ├── 0x4338665cbb7b2485a8855a139b75d5e34ab0db94.png
│ │ ├── 0x44754455564474a89358b2c2265883df993b12f0.png
│ │ ├── 0x47bead2563dcbf3bf2c9407fea4dc236faba485a.png
│ │ ├── 0x4b0f1812e5df2a09796481ff14017e6005508003.png
│ │ ├── 0x4bd17003473389a42daf6a0a729f6fdb328bbbd7.png
│ │ ├── 0x4cfbbdfbd5bf0814472ff35c72717bd095ada055.png
│ │ ├── 0x52ce071bd9b1c4b00a0b92d298c512478cad67e8.png
│ │ ├── 0x541e619858737031a1244a5d0cd47e5ef480342c.png
│ │ ├── 0x55d398326f99059ff775485246999027b3197955.png
│ │ ├── 0x5621b5a3f4a8008c4ccdd1b942b121c8b1944f1f.png
│ │ ├── 0x56b6fb708fc5732dec1afc8d8556423a2edccbd6.png
│ │ ├── 0x5921dee8556c4593eefcfad3ca5e2f618606483b.png
│ │ ├── 0x5986d5c77c65e5801a5caa4fae80089f870a71da.png
│ │ ├── 0x5F88AB06e8dfe89DF127B2430Bba4Af600866035.png
│ │ ├── 0x5ac52ee5b2a633895292ff6d8a89bb9190451587.png
│ │ ├── 0x5b6dcf557e2abe2323c48445e8cc948910d8c2c9.png
│ │ ├── 0x5d684adaf3fcfe9cfb5cede3abf02f0cdd1012e3.png
│ │ ├── 0x62D71B23bF15218C7d2D7E48DBbD9e9c650B173f.png
│ │ ├── 0x63870A18B6e42b01Ef1Ad8A2302ef50B7132054F.png
│ │ ├── 0x658A109C5900BC6d2357c87549B651670E5b0539.png
│ │ ├── 0x67ee3cb086f8a16f34bee3ca72fad36f7db929e2.png
│ │ ├── 0x695FD30aF473F2960e81Dc9bA7cB67679d35EDb7.png
│ │ ├── 0x6f769e65c14ebd1f68817f5f1dcdb61cfa2d6f7e.png
│ │ ├── 0x7083609fce4d1d8dc0c979aab8c869ea2c873402.png
│ │ ├── 0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c.png
│ │ ├── 0x71DE20e0C4616E7fcBfDD3f875d568492cBE4739.png
│ │ ├── 0x728C5baC3C3e370E372Fc4671f9ef6916b814d8B.png
│ │ ├── 0x72faa679e1008ad8382959ff48e392042a8b06f7.png
│ │ ├── 0x762539b45a1dcce3d36d080f74d1aed37844b878.png
│ │ ├── 0x78650b139471520656b9e7aa7a5e9276814a38e9.png
│ │ ├── 0x7a1da9f49224ef98389b071b8a3294d1cc5e3e6a.png
│ │ ├── 0x7a9f28eb62c791422aa23ceae1da9c847cbec9b0.png
│ │ ├── 0x7af173f350d916358af3e218bdf2178494beb748.png
│ │ ├── 0x7c17c8bed8d14bacce824d020f994f4880d6ab3b.png
│ │ ├── 0x7f70642d88cf1c4a3a7abb072b53b929b653eda5.png
│ │ ├── 0x8076c74c5e3f5852037f31ff0093eeb8c8add8d3.png
│ │ ├── 0x80d5f92c2c8c682070c95495313ddb680b267320.png
│ │ ├── 0x81859801b01764D4f0Fa5E64729f5a6C3b91435b.png
│ │ ├── 0x8443f091997f06a61670b735ed92734f5628692f.png
│ │ ├── 0x8519ea49c997f50ceffa444d240fb655e89248aa.png
│ │ ├── 0x857b222fc79e1cbbf8ca5f78cb133d1b7cf34bbd.png
│ │ ├── 0x88f1a5ae2a3bf98aeaf342d26b30a79438c9142e.png
│ │ ├── 0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d.png
│ │ ├── 0x8cd6e29d3686d24d3c2018cee54621ea0f89313b.png
│ │ ├── 0x8f0528ce5ef7b51152a59745befdd91d97091d2f.png
│ │ ├── 0x8ff795a6f4d97e7887c79bea79aba5cc76444adf.png
│ │ ├── 0x928e55daB735aa8260AF3cEDadA18B5f70C72f1b.png
│ │ ├── 0x92d7756c60dcfd4c689290e8a9f4d263b3b32241.png
│ │ ├── 0x947950bcc74888a40ffa2593c5798f11fc9124c4.png
│ │ ├── 0x948d2a81086a075b3130bac19e4c6dee1d2e3fe8.png
│ │ ├── 0x96058f8c3e16576d9bd68766f3836d9a33158f89.png
│ │ ├── 0x9678e42cebeb63f23197d726b29b1cb20d0064e5.png
│ │ ├── 0x96dd399f9c3afda1f194182f71600f1b65946501.png
│ │ ├── 0x9f589e3eabe42ebc94a44727b3f3531c0c877809.png
│ │ ├── 0xB67754f5b4C704A24d2db68e661b2875a4dDD197.png
│ │ ├── 0xC0eFf7749b125444953ef89682201Fb8c6A917CD.png
│ │ ├── 0xC7d8D35EBA58a0935ff2D5a33Df105DD9f071731.png
│ │ ├── 0xCa3F508B8e4Dd382eE878A314789373D80A5190A.png
│ │ ├── 0xE0e514c71282b6f4e823703a39374Cf58dc3eA4f.png
│ │ ├── 0xF215A127A196e3988C09d052e16BcFD365Cd7AA3.png
│ │ ├── 0xF35262a9d427F96d2437379eF090db986eaE5d42.png
│ │ ├── 0xa04F060077D90Fe2647B61e4dA4aD1F97d6649dc.png
│ │ ├── 0xa1303e6199b319a891b79685f0537d289af1fc83.png
│ │ ├── 0xa184088a740c695e156f91f5cc086a06bb78b827.png
│ │ ├── 0xa1faa113cbe53436df28ff0aee54275c13b40975.png
│ │ ├── 0xa2B726B1145A4773F68593CF171187d8EBe4d495.png
│ │ ├── 0xa7f552078dcc247c2684336020c03648500c6d9f.png
│ │ ├── 0xa8c2b8eec3d368c0253ad3dae65a5f2bbb89c929.png
│ │ ├── 0xac51066d7bec65dc4589368da368b212745d63e8.png
│ │ ├── 0xad6caeb32cd2c308980a548bd0bc5aa4306c6c18.png
│ │ ├── 0xae9269f27437f0fcbc232d39ec814844a51d6b8f.png
│ │ ├── 0xaf53d56ff99f1322515e54fdde93ff8b3b7dafd5.png
│ │ ├── 0xb2bd0749dbe21f623d9baba856d3b0f0e1bfec9c.png
│ │ ├── 0xb59490ab09a0f526cc7305822ac65f2ab12f9723.png
│ │ ├── 0xb86abcb37c3a4b64f74f59301aff131a1becc787.png
│ │ ├── 0xb8C540d00dd0Bf76ea12E4B4B95eFC90804f924E.png
│ │ ├── 0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c.png
│ │ ├── 0xbc5609612b7c44bef426de600b5fd1379db2ecf1.png
│ │ ├── 0xbcf39f0edda668c58371e519af37ca705f2bfcbd.png
│ │ ├── 0xbf5140a22578168fd562dccf235e5d43a02ce9b1.png
│ │ ├── 0xbf7c81fff98bbe61b40ed186e4afd6ddd01337fe.png
│ │ ├── 0xbfa0841f7a90c4ce6643f651756ee340991f99d5.png
│ │ ├── 0xc13b7a43223bb9bf4b69bd68ab20ca1b79d81c75.png
│ │ ├── 0xc3fed6eb39178a541d274e6fc748d48f0ca01cc3.png
│ │ ├── 0xc40c9a843e1c6d01b7578284a9028854f6683b1b.png
│ │ ├── 0xc53708664b99DF348dd27C3Ac0759d2DA9c40462.png
│ │ ├── 0xc5e6689c9c8b02be7c49912ef19e79cf24977f03.png
│ │ ├── 0xc9849e6fdb743d08faee3e34dd2d1bc69ea11a51.png
│ │ ├── 0xcd40f2670cf58720b694968698a5514e924f742d.png
│ │ ├── 0xcf6bb5389c92bdda8a3747ddb454cb7a64626c63.png
│ │ ├── 0xd41fdb03ba84762dd66a0af1a6c8540ff1ba5dfb.png
│ │ ├── 0xd4cb328a82bdf5f03eb737f37fa6b370aef3e888.png
│ │ ├── 0xdff8cb622790b7f92686c722b02cab55592f152c.png
│ │ ├── 0xe02df9e3e622debdd69fb838bb799e3f168902c5.png
│ │ ├── 0xe1d1f66215998786110ba0102ef558b22224c016.png
│ │ ├── 0xe40255c5d7fa7ceec5120408c78c787cecb4cfdb.png
│ │ ├── 0xe4ae305ebe1abe663f261bc00534067c80ad677c.png
│ │ ├── 0xe64f5cb844946c1f102bd25bbd87a5ab4ae89fbe.png
│ │ ├── 0xe9e7cea3dedca5984780bafc599bd69add087d56.png
│ │ ├── 0xeca41281c24451168a37211f0bc2b8645af45092.png
│ │ ├── 0xed28a457a5a76596ac48d87c0f577020f6ea1c4c.png
│ │ ├── 0xf05e45ad22150677a017fbd94b84fbb63dc9b44c.png
│ │ ├── 0xf0e406c49c63abf358030a299c0e00118c4c6ba5.png
│ │ ├── 0xf21768ccbc73ea5b6fd3c687208a7c2def2d966e.png
│ │ ├── 0xf218184af829cf2b0019f8e6f0b2423498a36983.png
│ │ ├── 0xf307910a4c7bbc79691fd374889b36d8531b08e3.png
│ │ ├── 0xf79037f6f6be66832de4e7516be52826bc3cbcc4.png
│ │ ├── 0xf859Bf77cBe8699013d6Dbc7C2b926Aaf307F830.png
│ │ ├── 0xf8a0bf9cf54bb92f17374d9e9a321e6a111a51bd.png
│ │ ├── 0xfCe146bF3146100cfe5dB4129cf6C82b0eF4Ad8c.png
│ │ ├── 0xfd7b3a77848f1c2d67e05e54d78d174a0c850335.png
│ │ └── bnb.png
│ ├── group-pancake.svg
│ ├── left-pancake.svg
│ ├── pancakeswap.png
│ └── right-pancake.svg
├── index.html
├── locales
│ ├── de.json
│ ├── en.json
│ ├── es-AR.json
│ ├── es-US.json
│ ├── it-IT.json
│ ├── iw.json
│ ├── ro.json
│ ├── ru.json
│ ├── vi.json
│ ├── zh-CN.json
│ └── zh-TW.json
├── manifest.json
└── swap.mp3
├── src
├── Providers.tsx
├── ThemeContext.tsx
├── components
│ ├── AddressInputPanel
│ │ └── index.tsx
│ ├── Card
│ │ └── index.tsx
│ ├── CardNav
│ │ └── index.tsx
│ ├── Column
│ │ └── index.tsx
│ ├── ConnectWalletButton
│ │ └── index.tsx
│ ├── CurrencyInputPanel
│ │ └── index.tsx
│ ├── CurrencyLogo
│ │ └── index.tsx
│ ├── DoubleLogo
│ │ └── index.tsx
│ ├── ListLogo
│ │ └── index.tsx
│ ├── Loader
│ │ └── index.tsx
│ ├── Logo
│ │ └── index.tsx
│ ├── Menu
│ │ ├── config.ts
│ │ └── index.tsx
│ ├── Modal
│ │ └── index.tsx
│ ├── NavigationTabs
│ │ └── index.tsx
│ ├── NumericalInput
│ │ └── index.tsx
│ ├── PageHeader
│ │ ├── AudioSetting.tsx
│ │ ├── RecentTransactionsModal.tsx
│ │ ├── SettingsModal.tsx
│ │ ├── SlippageToleranceSetting.tsx
│ │ ├── TransactionDeadlineSetting.tsx
│ │ └── index.tsx
│ ├── Pane
│ │ └── index.tsx
│ ├── Popover
│ │ └── index.tsx
│ ├── Popups
│ │ ├── ListUpdatePopup.tsx
│ │ ├── PopupItem.tsx
│ │ ├── TransactionPopup.tsx
│ │ └── index.tsx
│ ├── PositionCard
│ │ └── index.tsx
│ ├── ProgressSteps
│ │ └── index.tsx
│ ├── QuestionHelper
│ │ └── index.tsx
│ ├── Row
│ │ └── index.tsx
│ ├── SafeMoonWarningModal
│ │ └── index.tsx
│ ├── SearchModal
│ │ ├── CommonBases.tsx
│ │ ├── CurrencyList.tsx
│ │ ├── CurrencySearch.tsx
│ │ ├── CurrencySearchModal.tsx
│ │ ├── ListSelect.tsx
│ │ ├── SortButton.tsx
│ │ ├── filtering.ts
│ │ ├── sorting.ts
│ │ └── styleds.tsx
│ ├── Shared
│ │ ├── Common.tsx
│ │ └── index.tsx
│ ├── Slider
│ │ └── index.tsx
│ ├── SyrupWarningModal
│ │ └── index.tsx
│ ├── ToastListener
│ │ └── index.tsx
│ ├── TokenWarningModal
│ │ └── index.tsx
│ ├── Tooltip
│ │ └── index.tsx
│ ├── TransactionConfirmationModal
│ │ ├── ConfirmationModalContent.tsx
│ │ ├── ConfirmationPendingContent.tsx
│ │ ├── TransactionConfirmationModal.tsx
│ │ ├── TransactionErrorContent.tsx
│ │ ├── TransactionSubmittedContent.tsx
│ │ ├── helpers.tsx
│ │ └── index.tsx
│ ├── Web3ReactManager
│ │ └── index.tsx
│ ├── pancake
│ │ └── CoinLogo.ts
│ └── swap
│ │ ├── AdvancedSwapDetails.tsx
│ │ ├── AdvancedSwapDetailsDropdown.tsx
│ │ ├── ConfirmSwapModal.tsx
│ │ ├── FormattedPriceImpact.tsx
│ │ ├── SwapModalFooter.tsx
│ │ ├── SwapModalHeader.tsx
│ │ ├── SwapRoute.tsx
│ │ ├── TradePrice.tsx
│ │ ├── confirmPriceImpactWithoutFee.ts
│ │ └── styleds.tsx
├── connectors
│ ├── NetworkConnector.ts
│ └── index.ts
├── constants
│ ├── abis
│ │ ├── ens-public-resolver.json
│ │ ├── ens-registrar.json
│ │ ├── erc20.json
│ │ ├── erc20.ts
│ │ ├── erc20_bytes32.json
│ │ └── weth.json
│ ├── index.ts
│ ├── lists.ts
│ ├── localisation
│ │ └── languageCodes.ts
│ ├── multicall
│ │ ├── abi.json
│ │ └── index.ts
│ └── token
│ │ └── pancakeswap.json
├── data
│ ├── Allowances.ts
│ ├── Reserves.ts
│ └── TotalSupply.ts
├── hooks
│ ├── LanguageContext.ts
│ ├── Tokens.ts
│ ├── Trades.ts
│ ├── TranslationsContext.ts
│ ├── index.ts
│ ├── useApproveCallback.ts
│ ├── useAuth.ts
│ ├── useContract.ts
│ ├── useDebounce.ts
│ ├── useENS.ts
│ ├── useENSAddress.ts
│ ├── useENSContentHash.ts
│ ├── useENSName.ts
│ ├── useFetchListCallback.ts
│ ├── useGetDocumentTitlePrice.ts
│ ├── useGetLocalProfile.ts
│ ├── useGetPriceData.ts
│ ├── useHttpLocations.ts
│ ├── useI18n.ts
│ ├── useInterval.ts
│ ├── useIsWindowVisible.ts
│ ├── useLast.ts
│ ├── useOnClickOutside.tsx
│ ├── useParsedQueryString.ts
│ ├── useSwapCallback.ts
│ ├── useTheme.ts
│ ├── useToast.ts
│ ├── useToggle.ts
│ └── useWrapCallback.ts
├── i18n.ts
├── index.tsx
├── pages
│ ├── AddLiquidity
│ │ ├── ConfirmAddModalBottom.tsx
│ │ ├── PoolPriceBar.tsx
│ │ ├── index.tsx
│ │ └── redirects.tsx
│ ├── App.tsx
│ ├── AppBody.tsx
│ ├── Pool
│ │ ├── index.tsx
│ │ └── styleds.tsx
│ ├── PoolFinder
│ │ └── index.tsx
│ ├── RemoveLiquidity
│ │ ├── index.tsx
│ │ └── redirects.tsx
│ └── Swap
│ │ ├── index.tsx
│ │ └── redirects.tsx
├── react-app-env.d.ts
├── state
│ ├── actions.ts
│ ├── application
│ │ ├── actions.ts
│ │ ├── hooks.ts
│ │ ├── reducer.test.ts
│ │ ├── reducer.ts
│ │ └── updater.ts
│ ├── burn
│ │ ├── actions.ts
│ │ ├── hooks.ts
│ │ └── reducer.ts
│ ├── global
│ │ └── actions.ts
│ ├── index.ts
│ ├── lists
│ │ ├── actions.ts
│ │ ├── hooks.ts
│ │ ├── reducer.test.ts
│ │ ├── reducer.ts
│ │ └── updater.ts
│ ├── mint
│ │ ├── actions.ts
│ │ ├── hooks.ts
│ │ ├── reducer.test.ts
│ │ └── reducer.ts
│ ├── multicall
│ │ ├── actions.test.ts
│ │ ├── actions.ts
│ │ ├── hooks.ts
│ │ ├── reducer.test.ts
│ │ ├── reducer.ts
│ │ ├── updater.test.ts
│ │ └── updater.tsx
│ ├── swap
│ │ ├── actions.ts
│ │ ├── hooks.test.ts
│ │ ├── hooks.ts
│ │ ├── reducer.test.ts
│ │ └── reducer.ts
│ ├── toasts
│ │ └── index.ts
│ ├── transactions
│ │ ├── actions.ts
│ │ ├── hooks.tsx
│ │ ├── reducer.test.ts
│ │ ├── reducer.ts
│ │ ├── updater.test.ts
│ │ └── updater.tsx
│ ├── user
│ │ ├── actions.ts
│ │ ├── hooks.tsx
│ │ ├── reducer.test.ts
│ │ └── reducer.ts
│ └── wallet
│ │ └── hooks.ts
├── style
│ ├── Global.ts
│ └── styled.d.ts
└── utils
│ ├── chunkArray.test.ts
│ ├── chunkArray.ts
│ ├── contenthashToUri.test.skip.ts
│ ├── contenthashToUri.ts
│ ├── currencyId.ts
│ ├── getLibrary.ts
│ ├── getTokenList.ts
│ ├── index.test.ts
│ ├── index.ts
│ ├── isZero.ts
│ ├── listVersionLabel.ts
│ ├── maxAmountSpend.ts
│ ├── parseENSAddress.test.ts
│ ├── parseENSAddress.ts
│ ├── prices.test.ts
│ ├── prices.ts
│ ├── resolveENSContentHash.ts
│ ├── retry.test.ts
│ ├── retry.ts
│ ├── theme.ts
│ ├── translateTextHelpers.ts
│ ├── uriToHttp.test.ts
│ ├── uriToHttp.ts
│ ├── useDebouncedChangeHandler.ts
│ └── wrappedCurrency.ts
├── static.json
├── tsconfig.json
└── yarn.lock
/.commitlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["@commitlint/config-conventional"],
3 | "rules": {
4 | "subject-case": [2, "always", "sentence-case"],
5 | "type-enum": [
6 | 2,
7 | "always",
8 | ["build", "ci", "chore", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"]
9 | ]
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.env.development:
--------------------------------------------------------------------------------
1 | REACT_APP_NETWORK_URL="https://data-seed-prebsc-1-s1.binance.org:8545"
2 | REACT_APP_CHAIN_ID="97"
3 | REACT_APP_GTAG = "GTM-PXLD3XW"
4 |
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | REACT_APP_NETWORK_URL="https://bsc-dataseed1.defibit.io"
2 | REACT_APP_CHAIN_ID="56"
3 | REACT_APP_GTAG = "GTM-TLF66T4"
4 |
--------------------------------------------------------------------------------
/.env.test:
--------------------------------------------------------------------------------
1 | REACT_APP_NETWORK_URL="https://bsc-dataseed1.defibit.io"
2 | REACT_APP_CHAIN_ID="56"
3 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "settings": {
3 | "import/resolver": {
4 | "node": {
5 | "paths": ["./src"]
6 | }
7 | }
8 | },
9 | "env": {
10 | "es6": true,
11 | "browser": true,
12 | "jest": true
13 | },
14 |
15 | "extends": "@ricefarm/eslint-config-rice",
16 | "rules": {
17 | "no-console": ["warn", { "allow": ["info", "warn", "error"] }],
18 | "no-plusplus": 0,
19 | "prefer-destructuring": ["warn", { "object": true, "array": false }],
20 | "max-classes-per-file": 0,
21 | "camelcase": 0,
22 | "no-alert": 0,
23 | "no-underscore-dangle": 0,
24 | // Start temporary rules
25 | // These rules are here just to keep the lint error to 0 during the migration to the new rule set
26 | // They need to be removed and fixed as soon as possible
27 | "@typescript-eslint/ban-ts-comment": [1, { "ts-ignore": false, "ts-nocheck": false }],
28 | "@typescript-eslint/explicit-module-boundary-types": 0,
29 | "@typescript-eslint/no-explicit-any": 0,
30 | "radix": 0,
31 | "import/no-extraneous-dependencies": 0,
32 | "no-case-declarations": 0,
33 | "no-await-in-loop": 0,
34 | "no-param-reassign": 0,
35 | "no-nested-ternary": 0,
36 | "default-case": 0,
37 | "react/require-default-props": 0,
38 | "class-methods-use-this": 0
39 | // End temporary rules
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @RabbitDoge @hachiojidev @pancake-swap @Chef-Chungus @ChefKai
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 | ---
8 |
9 | **Bug Description**
10 | A clear and concise description of the bug.
11 |
12 | **Steps to Reproduce**
13 |
14 | 1. Go to ...
15 | 2. Click on ...
16 | ...
17 |
18 | **Expected Behavior**
19 | A clear and concise description of what you expected to happen.
20 |
21 | **Additional Context**
22 | Add any other context about the problem here (screenshots, whether the bug only occurs only in certain mobile/desktop/browser environments, etc.)
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 | **Is your feature request related to a problem? Please describe.**
10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
11 |
12 | **Describe the solution you'd like**
13 | A clear and concise description of what you want to happen.
14 |
15 | **Describe alternatives you've considered**
16 | A clear and concise description of any alternative solutions or features you've considered.
17 |
18 | **Additional context**
19 | Add any other context or screenshots about the feature request here.
20 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/listing-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Listing request
3 | about: Request a listing
4 | title: '[Listing] Request listing for {ADD TOKEN NAME HERE}'
5 | labels: listing
6 | assignees: Chef-Chungus
7 | ---
8 |
9 | ### DO NOT SUBMIT A LISTING REQUEST ON GITHUB - WE WILL LIST ANY PROJECT THAT HAS AN IFO/POOL/FARM
10 |
11 | Please apply here instead: https://docs.google.com/forms/d/e/1FAIpQLScGdT5rrVMr4WOWr08pvcroSeuIOtEJf1sVdQGVdcAOqryigQ/viewform
12 |
13 | Any Listing issues/PRs will be closed.
14 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/something-else.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Something Else
3 | about: Tell us something else
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | **PLEASE DO NOT SUBMIT TOKEN LISTING PULL REQUESTS**
2 |
3 | Any Listing PRs or Issues will be closed.
4 |
5 | Please apply here instead: https://docs.google.com/forms/d/e/1FAIpQLScGdT5rrVMr4WOWr08pvcroSeuIOtEJf1sVdQGVdcAOqryigQ/viewform
6 |
7 | Any Listing issues/PRs will be closed.
8 |
9 | ---
10 |
11 | [ ] Before opening a pull request, please read the [contributing guidelines](https://github.com/pancakeswap/pancake-swap-interface/blob/master/CONTRIBUTING.md) first
12 | [ ] If your PR is work in progress, open it as `draft`
13 | [ ] Before requesting a review, all the checks need to pass
14 | [ ] Explain what your PR does
15 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | commitlint:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | with:
12 | fetch-depth: 0
13 | - uses: wagoid/commitlint-github-action@v2
14 | with:
15 | configFile: .commitlintrc.json
16 | helpURL: https://docs.pancakeswap.finance/code/contributing#committing
17 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on:
3 | pull_request:
4 | branches:
5 | - master
6 |
7 | jobs:
8 | unit-tests:
9 | name: Unit tests
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v2
14 | - uses: actions/setup-node@v2
15 | with:
16 | node-version: '14'
17 | registry-url: https://registry.npmjs.org
18 | - run: yarn install --frozen-lockfile
19 | - run: yarn test
20 |
21 | integration-tests:
22 | name: Integration tests
23 | runs-on: ubuntu-latest
24 | steps:
25 | - name: Checkout
26 | uses: actions/checkout@v2
27 | - uses: actions/setup-node@v2
28 | with:
29 | node-version: '14'
30 | registry-url: https://registry.npmjs.org
31 | - run: yarn install --frozen-lockfile
32 | - run: yarn cypress install
33 | - run: yarn build
34 | - run: yarn integration-test
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # ide
2 | .idea/
3 |
4 | # Ignore crowdInApi details for translations - contact admin if you need these
5 | src/constants/localisation/crowdInApi.ts
6 |
7 | # See https://help.github.com/ignore-files/ for more about ignoring files.
8 | sftp-config.json
9 | # dependencies
10 | /node_modules
11 |
12 | # testing
13 | /coverage
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | .env.local
21 | .env.development.local
22 | .env.test.local
23 | .env.production.local
24 |
25 | /.netlify
26 |
27 | npm-debug.log*
28 | yarn-debug.log*
29 | yarn-error.log*
30 |
31 | notes.txt
32 | .idea/
33 |
34 | .vscode/
35 |
36 | package-lock.json
37 |
38 | cypress/videos
39 | cypress/screenshots
40 | cypress/fixtures/example.json
41 |
42 | .eslintcache
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "printWidth": 120
5 | }
6 |
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | ignore-scripts true
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to the Pancake ecosystem 🥞
2 |
3 | Thanks for taking the time to contribute !
4 | You can start by reading our [Contribution guidelines](https://docs.pancakeswap.finance/code/contributing) first.
5 |
6 | ## Setup
7 |
8 | Install the dependencies
9 |
10 | ```shell
11 | yarn
12 | yarn start
13 | ```
14 |
15 | Don't forget to setup your IDE with `eslint` and `prettier`.
16 |
17 | ## Change BSC network
18 |
19 | To change the BSC network from test net, modify the `REACT_APP_CHAIN_ID` value in `.env`.
20 |
21 | - MAIN NET `56`
22 | - TEST NET `97`
23 |
24 | ## Tests
25 |
26 | Firstly, if you need to install cypress
27 |
28 | ```js
29 | yarn cypress install
30 | ```
31 |
32 | Then to run the Cypress suite in CLI
33 |
34 | ```js
35 | yarn cypress run
36 | ```
37 |
38 | Or, to run Cypress with its GUI
39 |
40 | ```js
41 | yarn cypress open
42 | ```
43 |
44 | ## Localisation
45 |
46 | _In order for the Crowdin API queries to work - you will need `REACT_APP_CROWDIN_APIKEY` & `REACT_APP_CROWDIN_PROJECTID` env variables set in your root `.env.development.local` file_
47 |
48 | ### Adding translations
49 |
50 | A hook expose the function you need to translate content.
51 |
52 | ```
53 | import useI18n from 'hooks/useI18n'
54 | ...
55 | const TranslateString = useI18n()
56 | ...
57 | TranslateString(id, 'fallback')
58 | ```
59 |
60 | - **id** is the crowdin id of the string you want to translate.
61 | - **fallback** is a string fallback used if the id cannot be found.
62 |
63 | ### Variables
64 |
65 | The translation component can handle variables being passed in from Crowdin, with no code changes.
66 |
67 | It will only work if there is only **one** variable passed in, and if that variable within Crowdin is wrapped in **%** signs, i.e.:
68 |
69 | Translation in crowdin: `%asset% Earned` [link](https://crowdin.com/translate/pancakeswap/8/en-de#330)
70 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🍚 Rice Swap Exchange
2 |
3 | [](https://app.netlify.com/sites/swap-master/deploys)
4 |
5 | [PancakeSwap](https://pancakeswap.finance/) is an automated market maker (“**AMM**”) that allows two tokens to be exchanged on the [Binance Smart Chain](https://www.binance.org/en/smartChain) (BSC). It is fast, cheap, and allows anyone to participate.
6 |
7 | This repo is responsible for the **exchange** interface of the AMM: [exchange.pancakeswap.finance](https://exchange.pancakeswap.finance/)
8 |
9 | If you want to contribute, please refer to the [contributing guidelines](./CONTRIBUTING.md) of this project.
10 |
--------------------------------------------------------------------------------
/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "http://localhost:3000",
3 | "pluginsFile": false,
4 | "fixturesFolder": false,
5 | "supportFile": "cypress/support/index.js",
6 | "video": false,
7 | "defaultCommandTimeout": 10000
8 | }
9 |
--------------------------------------------------------------------------------
/cypress/integration/landing.test.ts:
--------------------------------------------------------------------------------
1 | import { TEST_ADDRESS_NEVER_USE_SHORTENED } from '../support/commands'
2 |
3 | describe('Landing Page', () => {
4 | beforeEach(() => cy.visit('/'))
5 | it('loads swap page', () => {
6 | cy.get('#swap-page')
7 | })
8 |
9 | it('redirects to url /swap', () => {
10 | cy.url().should('include', '/swap')
11 | })
12 |
13 | it('allows navigation to pool', () => {
14 | cy.get('#pool-nav-link').click()
15 | cy.url().should('include', '/pool')
16 | })
17 |
18 | // Wallet not connected - test will not pass.
19 | // it('is connected', () => {
20 | // cy.get('#web3-status-connected').click()
21 | // cy.get('#web3-account-identifier-row').contains(TEST_ADDRESS_NEVER_USE_SHORTENED)
22 | // })
23 | })
24 |
--------------------------------------------------------------------------------
/cypress/integration/pool.test.ts:
--------------------------------------------------------------------------------
1 | describe('Pool', () => {
2 | beforeEach(() => cy.visit('/pool'))
3 | it('add liquidity links to /add/BNB', () => {
4 | cy.get('#join-pool-button').click()
5 | cy.url().should('contain', '/add/BNB')
6 | })
7 |
8 | it('import pool links to /import', () => {
9 | cy.get('#import-pool-link').click()
10 | cy.url().should('contain', '/find')
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/cypress/integration/remove-liquidity.test.ts:
--------------------------------------------------------------------------------
1 | describe('Remove Liquidity', () => {
2 | it('redirects', () => {
3 | cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
4 | cy.url().should(
5 | 'contain',
6 | '/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
7 | )
8 | })
9 |
10 | it('bnb remove', () => {
11 | cy.visit('/remove/BNB/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82')
12 | cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'BNB')
13 | cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'CAKE')
14 | })
15 |
16 | it('bnb remove swap order', () => {
17 | cy.visit('/remove/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82/BNB')
18 | cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'CAKE')
19 | cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'BNB')
20 | })
21 |
22 | it('loads the two correct tokens', () => {
23 | cy.visit('/remove/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82-0xe9e7cea3dedca5984780bafc599bd69add087d56')
24 | cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'CAKE')
25 | cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'BUSD')
26 | })
27 |
28 | it('does not crash if CAKE is duplicated', () => {
29 | cy.visit('/remove/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82-0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82')
30 | cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'CAKE')
31 | cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'CAKE')
32 | })
33 |
34 | it('token not in storage is loaded', () => {
35 | cy.visit('/remove/0x7083609fce4d1d8dc0c979aab8c869ea2c873402-0x2170ed0880ac9a755fd29b2688956bd959f933f8')
36 | cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'DOT')
37 | cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'ETH')
38 | })
39 | })
40 |
--------------------------------------------------------------------------------
/cypress/integration/send.test.ts:
--------------------------------------------------------------------------------
1 | describe('Send', () => {
2 | it('should redirect', () => {
3 | cy.visit('/send')
4 | cy.url().should('include', '/swap')
5 | })
6 |
7 | it('should redirect with url params', () => {
8 | cy.visit('/send?outputCurrency=BNB&recipient=bob.argent.xyz')
9 | cy.url().should('contain', '/swap?outputCurrency=BNB&recipient=bob.argent.xyz')
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/cypress/integration/swap.test.ts:
--------------------------------------------------------------------------------
1 | describe('Swap', () => {
2 | beforeEach(() => {
3 | cy.visit('/swap')
4 | })
5 | it('can enter an amount into input', () => {
6 | cy.get('#swap-currency-input .token-amount-input')
7 | .type('0.001', { delay: 200 })
8 | .should('have.value', '0.001')
9 | })
10 |
11 | it('zero swap amount', () => {
12 | cy.get('#swap-currency-input .token-amount-input')
13 | .type('0.0', { delay: 200 })
14 | .should('have.value', '0.0')
15 | })
16 |
17 | it('invalid swap amount', () => {
18 | cy.get('#swap-currency-input .token-amount-input')
19 | .type('\\', { delay: 200 })
20 | .should('have.value', '')
21 | })
22 |
23 | it('can enter an amount into output', () => {
24 | cy.get('#swap-currency-output .token-amount-input')
25 | .type('0.001', { delay: 200 })
26 | .should('have.value', '0.001')
27 | })
28 |
29 | it('zero output amount', () => {
30 | cy.get('#swap-currency-output .token-amount-input')
31 | .type('0.0', { delay: 200 })
32 | .should('have.value', '0.0')
33 | })
34 |
35 | it('can swap BNB for ADA', () => {
36 | cy.get('#swap-currency-output .open-currency-select-button').click()
37 | cy.get('.token-item-0x3EE2200Efb3400fAbB9AacF31297cBdD1d435D47').click({ force: true })
38 | cy.get('#swap-currency-input .token-amount-input').should('be.visible')
39 | cy.get('#swap-currency-input .token-amount-input').type('0.001', { force: true, delay: 200 })
40 | cy.get('#swap-currency-output .token-amount-input').should('not.equal', '')
41 | // Wallet not connected so shows 'connect wallet', not swap-button.
42 | // cy.get('#swap-button').click()
43 | // cy.get('#confirm-swap-or-send').should('contain', 'Confirm Swap')
44 | })
45 |
46 | it('add a recipient does not exist unless in expert mode', () => {
47 | cy.get('#add-recipient-button').should('not.exist')
48 | })
49 | })
50 |
--------------------------------------------------------------------------------
/cypress/support/commands.d.ts:
--------------------------------------------------------------------------------
1 | export const TEST_ADDRESS_NEVER_USE: string
2 |
3 | export const TEST_ADDRESS_NEVER_USE_SHORTENED: string
4 |
5 | // declare namespace Cypress {
6 | // // eslint-disable-next-line @typescript-eslint/class-name-casing
7 | // interface cy {
8 | // additionalCommands(): void
9 | // }
10 | // }
11 |
--------------------------------------------------------------------------------
/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This file is processed and loaded automatically before your test files.
3 | //
4 | // You can read more here:
5 | // https://on.cypress.io/configuration
6 | // ***********************************************************
7 |
8 | // Import commands.ts using ES2015 syntax:
9 | import './commands'
10 |
--------------------------------------------------------------------------------
/cypress/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "baseUrl": "../node_modules",
5 | "target": "es5",
6 | "lib": [
7 | "es5",
8 | "dom"
9 | ],
10 | "types": [
11 | "cypress"
12 | ]
13 | },
14 | "include": [
15 | "**/*.ts"
16 | ]
17 | }
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/favicon.png
--------------------------------------------------------------------------------
/public/images/192x192_App_Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/192x192_App_Icon.png
--------------------------------------------------------------------------------
/public/images/512x512_App_Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/512x512_App_Icon.png
--------------------------------------------------------------------------------
/public/images/blue-loader.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/images/coins/0x007ea5c0ea75a8df45d288a4debdd5bb633f9e56.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x007ea5c0ea75a8df45d288a4debdd5bb633f9e56.png
--------------------------------------------------------------------------------
/public/images/coins/0x039cb485212f996a9dbb85a9a75d898f94d38da6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x039cb485212f996a9dbb85a9a75d898f94d38da6.png
--------------------------------------------------------------------------------
/public/images/coins/0x03ff0ff224f904be3118461335064bb48df47938.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x03ff0ff224f904be3118461335064bb48df47938.png
--------------------------------------------------------------------------------
/public/images/coins/0x04c747b40be4d535fc83d09939fb0f626f32800b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x04c747b40be4d535fc83d09939fb0f626f32800b.png
--------------------------------------------------------------------------------
/public/images/coins/0x0d8ce2a99bb6e3b7db580ed848240e4a0f9ae153.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x0d8ce2a99bb6e3b7db580ed848240e4a0f9ae153.png
--------------------------------------------------------------------------------
/public/images/coins/0x0d9319565be7f53CeFE84Ad201Be3f40feAE2740.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x0d9319565be7f53CeFE84Ad201Be3f40feAE2740.png
--------------------------------------------------------------------------------
/public/images/coins/0x0da6ed8b13214ff28e9ca979dd37439e8a88f6c4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x0da6ed8b13214ff28e9ca979dd37439e8a88f6c4.png
--------------------------------------------------------------------------------
/public/images/coins/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82.png
--------------------------------------------------------------------------------
/public/images/coins/0x0eb3a705fc54725037cc9e008bdede697f62f335.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x0eb3a705fc54725037cc9e008bdede697f62f335.png
--------------------------------------------------------------------------------
/public/images/coins/0x0f9e4d49f25de22c2202af916b681fbb3790497b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x0f9e4d49f25de22c2202af916b681fbb3790497b.png
--------------------------------------------------------------------------------
/public/images/coins/0x101d82428437127bf1608f699cd651e6abf9766e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x101d82428437127bf1608f699cd651e6abf9766e.png
--------------------------------------------------------------------------------
/public/images/coins/0x1203355742e76875154c0d13eb81dcd7711dc7d9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x1203355742e76875154c0d13eb81dcd7711dc7d9.png
--------------------------------------------------------------------------------
/public/images/coins/0x1613957159e9b0ac6c80e824f7eea748a32a0ae2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x1613957159e9b0ac6c80e824f7eea748a32a0ae2.png
--------------------------------------------------------------------------------
/public/images/coins/0x16939ef78684453bfdfb47825f8a5f714f12623a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x16939ef78684453bfdfb47825f8a5f714f12623a.png
--------------------------------------------------------------------------------
/public/images/coins/0x190b589cf9Fb8DDEabBFeae36a813FFb2A702454.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x190b589cf9Fb8DDEabBFeae36a813FFb2A702454.png
--------------------------------------------------------------------------------
/public/images/coins/0x1A2fb0Af670D0234c2857FaD35b789F8Cb725584.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x1A2fb0Af670D0234c2857FaD35b789F8Cb725584.png
--------------------------------------------------------------------------------
/public/images/coins/0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3.png
--------------------------------------------------------------------------------
/public/images/coins/0x1ba42e5193dfa8b03d15dd1b86a3113bbbef8eeb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x1ba42e5193dfa8b03d15dd1b86a3113bbbef8eeb.png
--------------------------------------------------------------------------------
/public/images/coins/0x1d1eb8e8293222e1a29d2c0e4ce6c0acfd89aaac.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x1d1eb8e8293222e1a29d2c0e4ce6c0acfd89aaac.png
--------------------------------------------------------------------------------
/public/images/coins/0x1d2f0da169ceb9fc7b3144628db156f3f6c60dbe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x1d2f0da169ceb9fc7b3144628db156f3f6c60dbe.png
--------------------------------------------------------------------------------
/public/images/coins/0x1f7216fdb338247512ec99715587bb97bbf96eae.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x1f7216fdb338247512ec99715587bb97bbf96eae.png
--------------------------------------------------------------------------------
/public/images/coins/0x1ffd0b47127fdd4097e54521c9e2c7f0d66aafc5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x1ffd0b47127fdd4097e54521c9e2c7f0d66aafc5.png
--------------------------------------------------------------------------------
/public/images/coins/0x2090c8295769791ab7A3CF1CC6e0AA19F35e441A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x2090c8295769791ab7A3CF1CC6e0AA19F35e441A.png
--------------------------------------------------------------------------------
/public/images/coins/0x211ffbe424b90e25a15531ca322adf1559779e45.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x211ffbe424b90e25a15531ca322adf1559779e45.png
--------------------------------------------------------------------------------
/public/images/coins/0x2170ed0880ac9a755fd29b2688956bd959f933f8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x2170ed0880ac9a755fd29b2688956bd959f933f8.png
--------------------------------------------------------------------------------
/public/images/coins/0x2222227e22102fe3322098e4cbfe18cfebd57c95.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x2222227e22102fe3322098e4cbfe18cfebd57c95.png
--------------------------------------------------------------------------------
/public/images/coins/0x23396cF899Ca06c4472205fC903bDB4de249D6fC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x23396cF899Ca06c4472205fC903bDB4de249D6fC.png
--------------------------------------------------------------------------------
/public/images/coins/0x233d91A0713155003fc4DcE0AFa871b508B3B715.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x233d91A0713155003fc4DcE0AFa871b508B3B715.png
--------------------------------------------------------------------------------
/public/images/coins/0x23e8a70534308a4AAF76fb8C32ec13d17a3BD89e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x23e8a70534308a4AAF76fb8C32ec13d17a3BD89e.png
--------------------------------------------------------------------------------
/public/images/coins/0x250632378e573c6be1ac2f97fcdf00515d0aa91b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x250632378e573c6be1ac2f97fcdf00515d0aa91b.png
--------------------------------------------------------------------------------
/public/images/coins/0x250b211EE44459dAd5Cd3bCa803dD6a7EcB5d46C.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x250b211EE44459dAd5Cd3bCa803dD6a7EcB5d46C.png
--------------------------------------------------------------------------------
/public/images/coins/0x25e9d05365c867e59c1904e7463af9f312296f9e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x25e9d05365c867e59c1904e7463af9f312296f9e.png
--------------------------------------------------------------------------------
/public/images/coins/0x26a5dfab467d4f58fb266648cae769503cec9580.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x26a5dfab467d4f58fb266648cae769503cec9580.png
--------------------------------------------------------------------------------
/public/images/coins/0x2cD1075682b0FCCaADd0Ca629e138E64015Ba11c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x2cD1075682b0FCCaADd0Ca629e138E64015Ba11c.png
--------------------------------------------------------------------------------
/public/images/coins/0x37dfACfaeDA801437Ff648A1559d73f4C40aAcb7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x37dfACfaeDA801437Ff648A1559d73f4C40aAcb7.png
--------------------------------------------------------------------------------
/public/images/coins/0x393B312C01048b3ed2720bF1B090084C09e408A1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x393B312C01048b3ed2720bF1B090084C09e408A1.png
--------------------------------------------------------------------------------
/public/images/coins/0x3947B992DC0147D2D89dF0392213781b04B25075.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x3947B992DC0147D2D89dF0392213781b04B25075.png
--------------------------------------------------------------------------------
/public/images/coins/0x3ee2200efb3400fabb9aacf31297cbdd1d435d47.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x3ee2200efb3400fabb9aacf31297cbdd1d435d47.png
--------------------------------------------------------------------------------
/public/images/coins/0x3fda9383a84c05ec8f7630fe10adf1fac13241cc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x3fda9383a84c05ec8f7630fe10adf1fac13241cc.png
--------------------------------------------------------------------------------
/public/images/coins/0x4131b87f74415190425ccd873048c708f8005823.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x4131b87f74415190425ccd873048c708f8005823.png
--------------------------------------------------------------------------------
/public/images/coins/0x42712dF5009c20fee340B245b510c0395896cF6e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x42712dF5009c20fee340B245b510c0395896cF6e.png
--------------------------------------------------------------------------------
/public/images/coins/0x42f6f551ae042cbe50c739158b4f0cac0edb9096.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x42f6f551ae042cbe50c739158b4f0cac0edb9096.png
--------------------------------------------------------------------------------
/public/images/coins/0x4338665cbb7b2485a8855a139b75d5e34ab0db94.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x4338665cbb7b2485a8855a139b75d5e34ab0db94.png
--------------------------------------------------------------------------------
/public/images/coins/0x44754455564474a89358b2c2265883df993b12f0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x44754455564474a89358b2c2265883df993b12f0.png
--------------------------------------------------------------------------------
/public/images/coins/0x47bead2563dcbf3bf2c9407fea4dc236faba485a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x47bead2563dcbf3bf2c9407fea4dc236faba485a.png
--------------------------------------------------------------------------------
/public/images/coins/0x4b0f1812e5df2a09796481ff14017e6005508003.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x4b0f1812e5df2a09796481ff14017e6005508003.png
--------------------------------------------------------------------------------
/public/images/coins/0x4bd17003473389a42daf6a0a729f6fdb328bbbd7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x4bd17003473389a42daf6a0a729f6fdb328bbbd7.png
--------------------------------------------------------------------------------
/public/images/coins/0x4cfbbdfbd5bf0814472ff35c72717bd095ada055.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x4cfbbdfbd5bf0814472ff35c72717bd095ada055.png
--------------------------------------------------------------------------------
/public/images/coins/0x52ce071bd9b1c4b00a0b92d298c512478cad67e8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x52ce071bd9b1c4b00a0b92d298c512478cad67e8.png
--------------------------------------------------------------------------------
/public/images/coins/0x541e619858737031a1244a5d0cd47e5ef480342c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x541e619858737031a1244a5d0cd47e5ef480342c.png
--------------------------------------------------------------------------------
/public/images/coins/0x55d398326f99059ff775485246999027b3197955.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x55d398326f99059ff775485246999027b3197955.png
--------------------------------------------------------------------------------
/public/images/coins/0x5621b5a3f4a8008c4ccdd1b942b121c8b1944f1f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x5621b5a3f4a8008c4ccdd1b942b121c8b1944f1f.png
--------------------------------------------------------------------------------
/public/images/coins/0x56b6fb708fc5732dec1afc8d8556423a2edccbd6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x56b6fb708fc5732dec1afc8d8556423a2edccbd6.png
--------------------------------------------------------------------------------
/public/images/coins/0x5921dee8556c4593eefcfad3ca5e2f618606483b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x5921dee8556c4593eefcfad3ca5e2f618606483b.png
--------------------------------------------------------------------------------
/public/images/coins/0x5986d5c77c65e5801a5caa4fae80089f870a71da.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x5986d5c77c65e5801a5caa4fae80089f870a71da.png
--------------------------------------------------------------------------------
/public/images/coins/0x5F88AB06e8dfe89DF127B2430Bba4Af600866035.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x5F88AB06e8dfe89DF127B2430Bba4Af600866035.png
--------------------------------------------------------------------------------
/public/images/coins/0x5ac52ee5b2a633895292ff6d8a89bb9190451587.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x5ac52ee5b2a633895292ff6d8a89bb9190451587.png
--------------------------------------------------------------------------------
/public/images/coins/0x5b6dcf557e2abe2323c48445e8cc948910d8c2c9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x5b6dcf557e2abe2323c48445e8cc948910d8c2c9.png
--------------------------------------------------------------------------------
/public/images/coins/0x5d684adaf3fcfe9cfb5cede3abf02f0cdd1012e3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x5d684adaf3fcfe9cfb5cede3abf02f0cdd1012e3.png
--------------------------------------------------------------------------------
/public/images/coins/0x62D71B23bF15218C7d2D7E48DBbD9e9c650B173f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x62D71B23bF15218C7d2D7E48DBbD9e9c650B173f.png
--------------------------------------------------------------------------------
/public/images/coins/0x63870A18B6e42b01Ef1Ad8A2302ef50B7132054F.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x63870A18B6e42b01Ef1Ad8A2302ef50B7132054F.png
--------------------------------------------------------------------------------
/public/images/coins/0x658A109C5900BC6d2357c87549B651670E5b0539.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x658A109C5900BC6d2357c87549B651670E5b0539.png
--------------------------------------------------------------------------------
/public/images/coins/0x67ee3cb086f8a16f34bee3ca72fad36f7db929e2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x67ee3cb086f8a16f34bee3ca72fad36f7db929e2.png
--------------------------------------------------------------------------------
/public/images/coins/0x695FD30aF473F2960e81Dc9bA7cB67679d35EDb7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x695FD30aF473F2960e81Dc9bA7cB67679d35EDb7.png
--------------------------------------------------------------------------------
/public/images/coins/0x6f769e65c14ebd1f68817f5f1dcdb61cfa2d6f7e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x6f769e65c14ebd1f68817f5f1dcdb61cfa2d6f7e.png
--------------------------------------------------------------------------------
/public/images/coins/0x7083609fce4d1d8dc0c979aab8c869ea2c873402.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x7083609fce4d1d8dc0c979aab8c869ea2c873402.png
--------------------------------------------------------------------------------
/public/images/coins/0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c.png
--------------------------------------------------------------------------------
/public/images/coins/0x71DE20e0C4616E7fcBfDD3f875d568492cBE4739.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x71DE20e0C4616E7fcBfDD3f875d568492cBE4739.png
--------------------------------------------------------------------------------
/public/images/coins/0x728C5baC3C3e370E372Fc4671f9ef6916b814d8B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x728C5baC3C3e370E372Fc4671f9ef6916b814d8B.png
--------------------------------------------------------------------------------
/public/images/coins/0x72faa679e1008ad8382959ff48e392042a8b06f7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x72faa679e1008ad8382959ff48e392042a8b06f7.png
--------------------------------------------------------------------------------
/public/images/coins/0x762539b45a1dcce3d36d080f74d1aed37844b878.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x762539b45a1dcce3d36d080f74d1aed37844b878.png
--------------------------------------------------------------------------------
/public/images/coins/0x78650b139471520656b9e7aa7a5e9276814a38e9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x78650b139471520656b9e7aa7a5e9276814a38e9.png
--------------------------------------------------------------------------------
/public/images/coins/0x7a1da9f49224ef98389b071b8a3294d1cc5e3e6a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x7a1da9f49224ef98389b071b8a3294d1cc5e3e6a.png
--------------------------------------------------------------------------------
/public/images/coins/0x7a9f28eb62c791422aa23ceae1da9c847cbec9b0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x7a9f28eb62c791422aa23ceae1da9c847cbec9b0.png
--------------------------------------------------------------------------------
/public/images/coins/0x7af173f350d916358af3e218bdf2178494beb748.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x7af173f350d916358af3e218bdf2178494beb748.png
--------------------------------------------------------------------------------
/public/images/coins/0x7c17c8bed8d14bacce824d020f994f4880d6ab3b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x7c17c8bed8d14bacce824d020f994f4880d6ab3b.png
--------------------------------------------------------------------------------
/public/images/coins/0x7f70642d88cf1c4a3a7abb072b53b929b653eda5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x7f70642d88cf1c4a3a7abb072b53b929b653eda5.png
--------------------------------------------------------------------------------
/public/images/coins/0x8076c74c5e3f5852037f31ff0093eeb8c8add8d3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x8076c74c5e3f5852037f31ff0093eeb8c8add8d3.png
--------------------------------------------------------------------------------
/public/images/coins/0x80d5f92c2c8c682070c95495313ddb680b267320.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x80d5f92c2c8c682070c95495313ddb680b267320.png
--------------------------------------------------------------------------------
/public/images/coins/0x81859801b01764D4f0Fa5E64729f5a6C3b91435b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x81859801b01764D4f0Fa5E64729f5a6C3b91435b.png
--------------------------------------------------------------------------------
/public/images/coins/0x8443f091997f06a61670b735ed92734f5628692f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x8443f091997f06a61670b735ed92734f5628692f.png
--------------------------------------------------------------------------------
/public/images/coins/0x8519ea49c997f50ceffa444d240fb655e89248aa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x8519ea49c997f50ceffa444d240fb655e89248aa.png
--------------------------------------------------------------------------------
/public/images/coins/0x857b222fc79e1cbbf8ca5f78cb133d1b7cf34bbd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x857b222fc79e1cbbf8ca5f78cb133d1b7cf34bbd.png
--------------------------------------------------------------------------------
/public/images/coins/0x88f1a5ae2a3bf98aeaf342d26b30a79438c9142e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x88f1a5ae2a3bf98aeaf342d26b30a79438c9142e.png
--------------------------------------------------------------------------------
/public/images/coins/0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d.png
--------------------------------------------------------------------------------
/public/images/coins/0x8cd6e29d3686d24d3c2018cee54621ea0f89313b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x8cd6e29d3686d24d3c2018cee54621ea0f89313b.png
--------------------------------------------------------------------------------
/public/images/coins/0x8f0528ce5ef7b51152a59745befdd91d97091d2f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x8f0528ce5ef7b51152a59745befdd91d97091d2f.png
--------------------------------------------------------------------------------
/public/images/coins/0x8ff795a6f4d97e7887c79bea79aba5cc76444adf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x8ff795a6f4d97e7887c79bea79aba5cc76444adf.png
--------------------------------------------------------------------------------
/public/images/coins/0x928e55daB735aa8260AF3cEDadA18B5f70C72f1b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x928e55daB735aa8260AF3cEDadA18B5f70C72f1b.png
--------------------------------------------------------------------------------
/public/images/coins/0x92d7756c60dcfd4c689290e8a9f4d263b3b32241.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x92d7756c60dcfd4c689290e8a9f4d263b3b32241.png
--------------------------------------------------------------------------------
/public/images/coins/0x947950bcc74888a40ffa2593c5798f11fc9124c4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x947950bcc74888a40ffa2593c5798f11fc9124c4.png
--------------------------------------------------------------------------------
/public/images/coins/0x948d2a81086a075b3130bac19e4c6dee1d2e3fe8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x948d2a81086a075b3130bac19e4c6dee1d2e3fe8.png
--------------------------------------------------------------------------------
/public/images/coins/0x96058f8c3e16576d9bd68766f3836d9a33158f89.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x96058f8c3e16576d9bd68766f3836d9a33158f89.png
--------------------------------------------------------------------------------
/public/images/coins/0x9678e42cebeb63f23197d726b29b1cb20d0064e5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x9678e42cebeb63f23197d726b29b1cb20d0064e5.png
--------------------------------------------------------------------------------
/public/images/coins/0x96dd399f9c3afda1f194182f71600f1b65946501.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x96dd399f9c3afda1f194182f71600f1b65946501.png
--------------------------------------------------------------------------------
/public/images/coins/0x9f589e3eabe42ebc94a44727b3f3531c0c877809.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0x9f589e3eabe42ebc94a44727b3f3531c0c877809.png
--------------------------------------------------------------------------------
/public/images/coins/0xB67754f5b4C704A24d2db68e661b2875a4dDD197.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xB67754f5b4C704A24d2db68e661b2875a4dDD197.png
--------------------------------------------------------------------------------
/public/images/coins/0xC0eFf7749b125444953ef89682201Fb8c6A917CD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xC0eFf7749b125444953ef89682201Fb8c6A917CD.png
--------------------------------------------------------------------------------
/public/images/coins/0xC7d8D35EBA58a0935ff2D5a33Df105DD9f071731.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xC7d8D35EBA58a0935ff2D5a33Df105DD9f071731.png
--------------------------------------------------------------------------------
/public/images/coins/0xCa3F508B8e4Dd382eE878A314789373D80A5190A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xCa3F508B8e4Dd382eE878A314789373D80A5190A.png
--------------------------------------------------------------------------------
/public/images/coins/0xE0e514c71282b6f4e823703a39374Cf58dc3eA4f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xE0e514c71282b6f4e823703a39374Cf58dc3eA4f.png
--------------------------------------------------------------------------------
/public/images/coins/0xF215A127A196e3988C09d052e16BcFD365Cd7AA3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xF215A127A196e3988C09d052e16BcFD365Cd7AA3.png
--------------------------------------------------------------------------------
/public/images/coins/0xF35262a9d427F96d2437379eF090db986eaE5d42.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xF35262a9d427F96d2437379eF090db986eaE5d42.png
--------------------------------------------------------------------------------
/public/images/coins/0xa04F060077D90Fe2647B61e4dA4aD1F97d6649dc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xa04F060077D90Fe2647B61e4dA4aD1F97d6649dc.png
--------------------------------------------------------------------------------
/public/images/coins/0xa1303e6199b319a891b79685f0537d289af1fc83.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xa1303e6199b319a891b79685f0537d289af1fc83.png
--------------------------------------------------------------------------------
/public/images/coins/0xa184088a740c695e156f91f5cc086a06bb78b827.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xa184088a740c695e156f91f5cc086a06bb78b827.png
--------------------------------------------------------------------------------
/public/images/coins/0xa1faa113cbe53436df28ff0aee54275c13b40975.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xa1faa113cbe53436df28ff0aee54275c13b40975.png
--------------------------------------------------------------------------------
/public/images/coins/0xa2B726B1145A4773F68593CF171187d8EBe4d495.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xa2B726B1145A4773F68593CF171187d8EBe4d495.png
--------------------------------------------------------------------------------
/public/images/coins/0xa7f552078dcc247c2684336020c03648500c6d9f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xa7f552078dcc247c2684336020c03648500c6d9f.png
--------------------------------------------------------------------------------
/public/images/coins/0xa8c2b8eec3d368c0253ad3dae65a5f2bbb89c929.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xa8c2b8eec3d368c0253ad3dae65a5f2bbb89c929.png
--------------------------------------------------------------------------------
/public/images/coins/0xac51066d7bec65dc4589368da368b212745d63e8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xac51066d7bec65dc4589368da368b212745d63e8.png
--------------------------------------------------------------------------------
/public/images/coins/0xad6caeb32cd2c308980a548bd0bc5aa4306c6c18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xad6caeb32cd2c308980a548bd0bc5aa4306c6c18.png
--------------------------------------------------------------------------------
/public/images/coins/0xae9269f27437f0fcbc232d39ec814844a51d6b8f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xae9269f27437f0fcbc232d39ec814844a51d6b8f.png
--------------------------------------------------------------------------------
/public/images/coins/0xaf53d56ff99f1322515e54fdde93ff8b3b7dafd5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xaf53d56ff99f1322515e54fdde93ff8b3b7dafd5.png
--------------------------------------------------------------------------------
/public/images/coins/0xb2bd0749dbe21f623d9baba856d3b0f0e1bfec9c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xb2bd0749dbe21f623d9baba856d3b0f0e1bfec9c.png
--------------------------------------------------------------------------------
/public/images/coins/0xb59490ab09a0f526cc7305822ac65f2ab12f9723.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xb59490ab09a0f526cc7305822ac65f2ab12f9723.png
--------------------------------------------------------------------------------
/public/images/coins/0xb86abcb37c3a4b64f74f59301aff131a1becc787.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xb86abcb37c3a4b64f74f59301aff131a1becc787.png
--------------------------------------------------------------------------------
/public/images/coins/0xb8C540d00dd0Bf76ea12E4B4B95eFC90804f924E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xb8C540d00dd0Bf76ea12E4B4B95eFC90804f924E.png
--------------------------------------------------------------------------------
/public/images/coins/0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c.png
--------------------------------------------------------------------------------
/public/images/coins/0xbc5609612b7c44bef426de600b5fd1379db2ecf1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xbc5609612b7c44bef426de600b5fd1379db2ecf1.png
--------------------------------------------------------------------------------
/public/images/coins/0xbcf39f0edda668c58371e519af37ca705f2bfcbd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xbcf39f0edda668c58371e519af37ca705f2bfcbd.png
--------------------------------------------------------------------------------
/public/images/coins/0xbf5140a22578168fd562dccf235e5d43a02ce9b1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xbf5140a22578168fd562dccf235e5d43a02ce9b1.png
--------------------------------------------------------------------------------
/public/images/coins/0xbf7c81fff98bbe61b40ed186e4afd6ddd01337fe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xbf7c81fff98bbe61b40ed186e4afd6ddd01337fe.png
--------------------------------------------------------------------------------
/public/images/coins/0xbfa0841f7a90c4ce6643f651756ee340991f99d5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xbfa0841f7a90c4ce6643f651756ee340991f99d5.png
--------------------------------------------------------------------------------
/public/images/coins/0xc13b7a43223bb9bf4b69bd68ab20ca1b79d81c75.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xc13b7a43223bb9bf4b69bd68ab20ca1b79d81c75.png
--------------------------------------------------------------------------------
/public/images/coins/0xc3fed6eb39178a541d274e6fc748d48f0ca01cc3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xc3fed6eb39178a541d274e6fc748d48f0ca01cc3.png
--------------------------------------------------------------------------------
/public/images/coins/0xc40c9a843e1c6d01b7578284a9028854f6683b1b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xc40c9a843e1c6d01b7578284a9028854f6683b1b.png
--------------------------------------------------------------------------------
/public/images/coins/0xc53708664b99DF348dd27C3Ac0759d2DA9c40462.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xc53708664b99DF348dd27C3Ac0759d2DA9c40462.png
--------------------------------------------------------------------------------
/public/images/coins/0xc5e6689c9c8b02be7c49912ef19e79cf24977f03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xc5e6689c9c8b02be7c49912ef19e79cf24977f03.png
--------------------------------------------------------------------------------
/public/images/coins/0xc9849e6fdb743d08faee3e34dd2d1bc69ea11a51.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xc9849e6fdb743d08faee3e34dd2d1bc69ea11a51.png
--------------------------------------------------------------------------------
/public/images/coins/0xcd40f2670cf58720b694968698a5514e924f742d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xcd40f2670cf58720b694968698a5514e924f742d.png
--------------------------------------------------------------------------------
/public/images/coins/0xcf6bb5389c92bdda8a3747ddb454cb7a64626c63.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xcf6bb5389c92bdda8a3747ddb454cb7a64626c63.png
--------------------------------------------------------------------------------
/public/images/coins/0xd41fdb03ba84762dd66a0af1a6c8540ff1ba5dfb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xd41fdb03ba84762dd66a0af1a6c8540ff1ba5dfb.png
--------------------------------------------------------------------------------
/public/images/coins/0xd4cb328a82bdf5f03eb737f37fa6b370aef3e888.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xd4cb328a82bdf5f03eb737f37fa6b370aef3e888.png
--------------------------------------------------------------------------------
/public/images/coins/0xdff8cb622790b7f92686c722b02cab55592f152c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xdff8cb622790b7f92686c722b02cab55592f152c.png
--------------------------------------------------------------------------------
/public/images/coins/0xe02df9e3e622debdd69fb838bb799e3f168902c5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xe02df9e3e622debdd69fb838bb799e3f168902c5.png
--------------------------------------------------------------------------------
/public/images/coins/0xe1d1f66215998786110ba0102ef558b22224c016.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xe1d1f66215998786110ba0102ef558b22224c016.png
--------------------------------------------------------------------------------
/public/images/coins/0xe40255c5d7fa7ceec5120408c78c787cecb4cfdb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xe40255c5d7fa7ceec5120408c78c787cecb4cfdb.png
--------------------------------------------------------------------------------
/public/images/coins/0xe4ae305ebe1abe663f261bc00534067c80ad677c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xe4ae305ebe1abe663f261bc00534067c80ad677c.png
--------------------------------------------------------------------------------
/public/images/coins/0xe64f5cb844946c1f102bd25bbd87a5ab4ae89fbe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xe64f5cb844946c1f102bd25bbd87a5ab4ae89fbe.png
--------------------------------------------------------------------------------
/public/images/coins/0xe9e7cea3dedca5984780bafc599bd69add087d56.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xe9e7cea3dedca5984780bafc599bd69add087d56.png
--------------------------------------------------------------------------------
/public/images/coins/0xeca41281c24451168a37211f0bc2b8645af45092.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xeca41281c24451168a37211f0bc2b8645af45092.png
--------------------------------------------------------------------------------
/public/images/coins/0xed28a457a5a76596ac48d87c0f577020f6ea1c4c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xed28a457a5a76596ac48d87c0f577020f6ea1c4c.png
--------------------------------------------------------------------------------
/public/images/coins/0xf05e45ad22150677a017fbd94b84fbb63dc9b44c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xf05e45ad22150677a017fbd94b84fbb63dc9b44c.png
--------------------------------------------------------------------------------
/public/images/coins/0xf0e406c49c63abf358030a299c0e00118c4c6ba5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xf0e406c49c63abf358030a299c0e00118c4c6ba5.png
--------------------------------------------------------------------------------
/public/images/coins/0xf21768ccbc73ea5b6fd3c687208a7c2def2d966e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xf21768ccbc73ea5b6fd3c687208a7c2def2d966e.png
--------------------------------------------------------------------------------
/public/images/coins/0xf218184af829cf2b0019f8e6f0b2423498a36983.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xf218184af829cf2b0019f8e6f0b2423498a36983.png
--------------------------------------------------------------------------------
/public/images/coins/0xf307910a4c7bbc79691fd374889b36d8531b08e3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xf307910a4c7bbc79691fd374889b36d8531b08e3.png
--------------------------------------------------------------------------------
/public/images/coins/0xf79037f6f6be66832de4e7516be52826bc3cbcc4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xf79037f6f6be66832de4e7516be52826bc3cbcc4.png
--------------------------------------------------------------------------------
/public/images/coins/0xf859Bf77cBe8699013d6Dbc7C2b926Aaf307F830.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xf859Bf77cBe8699013d6Dbc7C2b926Aaf307F830.png
--------------------------------------------------------------------------------
/public/images/coins/0xf8a0bf9cf54bb92f17374d9e9a321e6a111a51bd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xf8a0bf9cf54bb92f17374d9e9a321e6a111a51bd.png
--------------------------------------------------------------------------------
/public/images/coins/0xfCe146bF3146100cfe5dB4129cf6C82b0eF4Ad8c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xfCe146bF3146100cfe5dB4129cf6C82b0eF4Ad8c.png
--------------------------------------------------------------------------------
/public/images/coins/0xfd7b3a77848f1c2d67e05e54d78d174a0c850335.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/0xfd7b3a77848f1c2d67e05e54d78d174a0c850335.png
--------------------------------------------------------------------------------
/public/images/coins/bnb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/coins/bnb.png
--------------------------------------------------------------------------------
/public/images/pancakeswap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/images/pancakeswap.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "PancakeSwap",
3 | "name": "PancakeSwap",
4 | "icons": [
5 | {
6 | "src": "./images/192x192_App_Icon.png",
7 | "sizes": "192x192",
8 | "type": "image/png",
9 | "purpose": "any maskable"
10 | },
11 | {
12 | "src": "./images/512x512_App_Icon.png",
13 | "sizes": "512x512",
14 | "type": "image/png",
15 | "purpose": "any maskable"
16 | }
17 | ],
18 | "orientation": "portrait",
19 | "display": "standalone",
20 | "theme_color": "#000000",
21 | "background_color": "#ffffff"
22 | }
23 |
--------------------------------------------------------------------------------
/public/swap.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rice-farm/rice-swap-interface/acdb3c3d973b356ecfd934e91970912587afe3e9/public/swap.mp3
--------------------------------------------------------------------------------
/src/Providers.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { createWeb3ReactRoot, Web3ReactProvider } from '@web3-react/core'
3 | import { Provider } from 'react-redux'
4 | import { ModalProvider } from '@pancakeswap-libs/uikit'
5 | import { NetworkContextName } from './constants'
6 | import store from './state'
7 | import getLibrary from './utils/getLibrary'
8 | import { ThemeContextProvider } from './ThemeContext'
9 |
10 | const Web3ProviderNetwork = createWeb3ReactRoot(NetworkContextName)
11 |
12 | const Providers: React.FC = ({ children }) => {
13 | return (
14 |
15 |
16 |
17 |
18 | {children}
19 |
20 |
21 |
22 |
23 | )
24 | }
25 |
26 | export default Providers
27 |
--------------------------------------------------------------------------------
/src/ThemeContext.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { ThemeProvider as SCThemeProvider } from 'styled-components'
3 | import { light, dark } from '@pancakeswap-libs/uikit'
4 |
5 | const CACHE_KEY = 'IS_DARK'
6 |
7 | export interface ThemeContextType {
8 | isDark: boolean;
9 | toggleTheme: () => void;
10 | }
11 |
12 | const ThemeContext = React.createContext({ isDark: false, toggleTheme: () => null })
13 |
14 | const ThemeContextProvider: React.FC = ({ children }) => {
15 | const [isDark, setIsDark] = useState(() => {
16 | const isDarkUserSetting = localStorage.getItem(CACHE_KEY)
17 | return isDarkUserSetting ? JSON.parse(isDarkUserSetting) : false
18 | })
19 |
20 | const toggleTheme = () => {
21 | setIsDark((prevState: any) => {
22 | localStorage.setItem(CACHE_KEY, JSON.stringify(!prevState))
23 | return !prevState
24 | })
25 | }
26 |
27 | return (
28 |
29 | {children}
30 |
31 | )
32 | }
33 |
34 | export { ThemeContext, ThemeContextProvider }
35 |
--------------------------------------------------------------------------------
/src/components/Card/index.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const Card = styled.div`
4 | width: 100%;
5 | border-radius: 16px;
6 | padding: 1.25rem;
7 | padding: ${({ padding }) => padding};
8 | border: ${({ border }) => border};
9 | border-radius: ${({ borderRadius }) => borderRadius};
10 | `
11 | export default Card
12 |
13 | export const LightCard = styled(Card)`
14 | border: 1px solid ${({ theme }) => theme.colors.invertedContrast};
15 | background-color: ${({ theme }) => theme.colors.invertedContrast};
16 | `
17 |
18 | export const GreyCard = styled(Card)`
19 | background-color: ${({ theme }) => theme.colors.tertiary};
20 | `
21 |
--------------------------------------------------------------------------------
/src/components/CardNav/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { Link } from 'react-router-dom'
4 | import { ButtonMenu, ButtonMenuItem } from '@pancakeswap-libs/uikit'
5 | import useI18n from 'hooks/useI18n'
6 |
7 | const StyledNav = styled.div`
8 | margin-bottom: 40px;
9 | `
10 |
11 | function Nav({ activeIndex = 0 }: { activeIndex?: number }) {
12 | const TranslateString = useI18n()
13 | return (
14 |
15 |
16 |
17 | {TranslateString(1142, 'Swap')}
18 |
19 |
20 | {TranslateString(262, 'Liquidity')}
21 |
22 |
29 | Bridge
30 |
31 |
32 |
33 | )
34 | }
35 |
36 | export default Nav
37 |
--------------------------------------------------------------------------------
/src/components/Column/index.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const Column = styled.div`
4 | display: flex;
5 | flex-direction: column;
6 | justify-content: flex-start;
7 | `
8 | export const ColumnCenter = styled(Column)`
9 | width: 100%;
10 | align-items: center;
11 | `
12 |
13 | export const AutoColumn = styled.div<{
14 | gap?: 'sm' | 'md' | 'lg' | string
15 | justify?: 'stretch' | 'center' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'space-between'
16 | }>`
17 | display: grid;
18 | grid-auto-rows: auto;
19 | grid-row-gap: ${({ gap }) => (gap === 'sm' && '8px') || (gap === 'md' && '12px') || (gap === 'lg' && '24px') || gap};
20 | justify-items: ${({ justify }) => justify && justify};
21 | `
22 |
23 | export default Column
24 |
--------------------------------------------------------------------------------
/src/components/ConnectWalletButton/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Button, ButtonProps, useWalletModal} from '@pancakeswap-libs/uikit'
3 | import useI18n from 'hooks/useI18n'
4 | import useAuth from 'hooks/useAuth'
5 |
6 | const UnlockButton: React.FC = (props) => {
7 | const TranslateString = useI18n()
8 | const { login, logout } = useAuth()
9 | const { onPresentConnectModal } = useWalletModal(login, logout)
10 |
11 | return (
12 |
15 | )
16 | }
17 |
18 | export default UnlockButton
19 |
--------------------------------------------------------------------------------
/src/components/CurrencyLogo/index.tsx:
--------------------------------------------------------------------------------
1 | import { Currency, ETHER, Token } from '@pancakeswap-libs/sdk'
2 | import React, { useMemo } from 'react'
3 | import styled from 'styled-components'
4 | import useHttpLocations from '../../hooks/useHttpLocations'
5 | import { WrappedTokenInfo } from '../../state/lists/hooks'
6 | import Logo from '../Logo'
7 | import CoinLogo from '../pancake/CoinLogo'
8 |
9 | const getTokenLogoURL = (address: string) =>
10 | `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/smartchain/assets/${address}/logo.png`
11 |
12 | const StyledBnbLogo = styled.img<{ size: string }>`
13 | width: ${({ size }) => size};
14 | height: ${({ size }) => size};
15 | box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
16 | border-radius: 24px;
17 | `
18 |
19 | const StyledLogo = styled(Logo)<{ size: string }>`
20 | width: ${({ size }) => size};
21 | height: ${({ size }) => size};
22 | `
23 |
24 | export default function CurrencyLogo({
25 | currency,
26 | size = '24px',
27 | style,
28 | }: {
29 | currency?: Currency
30 | size?: string
31 | style?: React.CSSProperties
32 | }) {
33 | const uriLocations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined)
34 |
35 | const srcs: string[] = useMemo(() => {
36 | if (currency === ETHER) return []
37 |
38 | if (currency instanceof Token) {
39 | if (currency instanceof WrappedTokenInfo) {
40 | return [...uriLocations, `/images/coins/${currency?.address ?? 'token'}.png`, getTokenLogoURL(currency.address)]
41 | }
42 |
43 | return [`/images/coins/${currency?.address ?? 'token'}.png`, getTokenLogoURL(currency.address)]
44 | }
45 | return []
46 | }, [currency, uriLocations])
47 |
48 | if (currency === ETHER) {
49 | return
50 | }
51 |
52 | return (currency as any)?.symbol ? (
53 |
54 | ) : (
55 |
56 | )
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/DoubleLogo/index.tsx:
--------------------------------------------------------------------------------
1 | import { Currency } from '@pancakeswap-libs/sdk'
2 | import React from 'react'
3 | import styled from 'styled-components'
4 | import CurrencyLogo from '../CurrencyLogo'
5 |
6 | const Wrapper = styled.div<{ margin: boolean; sizeraw: number }>`
7 | position: relative;
8 | display: flex;
9 | flex-direction: row;
10 | margin-right: ${({ sizeraw, margin }) => margin && `${(sizeraw / 3 + 8).toString() }px`};
11 | `
12 |
13 | interface DoubleCurrencyLogoProps {
14 | margin?: boolean
15 | size?: number
16 | currency0?: Currency
17 | currency1?: Currency
18 | }
19 |
20 | const HigherLogo = styled(CurrencyLogo)`
21 | z-index: 2;
22 | `
23 | const CoveredLogo = styled(CurrencyLogo)<{ sizeraw: number }>`
24 | position: absolute;
25 | left: ${({ sizeraw }) => `${(sizeraw / 2).toString() }px`};
26 | `
27 |
28 | export default function DoubleCurrencyLogo({
29 | currency0,
30 | currency1,
31 | size = 16,
32 | margin = false
33 | }: DoubleCurrencyLogoProps) {
34 | return (
35 |
36 | {currency0 && }
37 | {currency1 && }
38 |
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/ListLogo/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import useHttpLocations from '../../hooks/useHttpLocations'
4 |
5 | import Logo from '../Logo'
6 |
7 | const StyledListLogo = styled(Logo)<{ size: string }>`
8 | width: ${({ size }) => size};
9 | height: ${({ size }) => size};
10 | `
11 |
12 | export default function ListLogo({
13 | logoURI,
14 | style,
15 | size = '24px',
16 | alt
17 | }: {
18 | logoURI: string
19 | size?: string
20 | style?: React.CSSProperties
21 | alt?: string
22 | }) {
23 | const srcs: string[] = useHttpLocations(logoURI)
24 |
25 | return
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/Loader/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import styled, { keyframes } from 'styled-components'
4 |
5 | const rotate = keyframes`
6 | from {
7 | transform: rotate(0deg);
8 | }
9 | to {
10 | transform: rotate(360deg);
11 | }
12 | `
13 |
14 | const StyledSVG = styled.svg<{ size: string; stroke?: string }>`
15 | animation: 2s ${rotate} linear infinite;
16 | height: ${({ size }) => size};
17 | width: ${({ size }) => size};
18 | path {
19 | stroke: ${({ stroke, theme }) => stroke ?? theme.colors.primary};
20 | }
21 | `
22 |
23 | /**
24 | * Takes in custom size and stroke for circle color, default to primary color as fill,
25 | * need ...rest for layered styles on top
26 | */
27 | export default function Loader({ size = '16px', stroke, ...rest }: { size?: string; stroke?: string }) {
28 | return (
29 |
30 |
36 |
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/Logo/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { HelpCircle } from 'react-feather'
3 |
4 | const BAD_SRCS: { [tokenAddress: string]: true } = {}
5 |
6 | export interface LogoProps {
7 | alt?: string
8 | style?: any
9 | className?: string
10 | srcs: string[]
11 | }
12 |
13 | /**
14 | * Renders an image by sequentially trying a list of URIs, and then eventually a fallback triangle alert
15 | */
16 | export default function Logo({ srcs, alt, ...rest }: LogoProps) {
17 | const [, refresh] = useState(0)
18 |
19 | const src: string | undefined = srcs.find((s) => !BAD_SRCS[s])
20 |
21 | if (src) {
22 | return (
23 |
{
28 | if (src) BAD_SRCS[src] = true
29 | refresh((i) => i + 1)
30 | }}
31 | />
32 | )
33 | }
34 |
35 | return
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/Menu/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { Menu as UikitMenu} from '@pancakeswap-libs/uikit'
3 | import { useWeb3React } from '@web3-react/core'
4 | import { allLanguages } from 'constants/localisation/languageCodes'
5 | import { LanguageContext } from 'hooks/LanguageContext'
6 | import useTheme from 'hooks/useTheme'
7 | import useGetPriceData from 'hooks/useGetPriceData'
8 | import useGetLocalProfile from 'hooks/useGetLocalProfile'
9 | import useAuth from 'hooks/useAuth'
10 | import links from './config'
11 | import { CAKE } from '../../constants'
12 |
13 | const Menu: React.FC = (props) => {
14 | const { account } = useWeb3React()
15 | const { login, logout } = useAuth()
16 | const { selectedLanguage, setSelectedLanguage } = useContext(LanguageContext)
17 | const { isDark, toggleTheme } = useTheme()
18 | const priceData = useGetPriceData()
19 | const cakePriceUsd = priceData ? Number(priceData.data[CAKE.address].price) : undefined
20 | const profile = useGetLocalProfile()
21 |
22 | return (
23 |
37 | )
38 | }
39 |
40 | export default Menu
41 |
--------------------------------------------------------------------------------
/src/components/PageHeader/AudioSetting.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box, Flex, Text, PancakeToggle, useMatchBreakpoints } from '@pancakeswap-libs/uikit'
3 | import { useAudioModeManager } from 'state/user/hooks'
4 |
5 | type AudioSettingModalProps = {
6 | translateString: (translationId: number, fallback: string) => string
7 | }
8 |
9 | const AudioSetting = ({ translateString }: AudioSettingModalProps) => {
10 | const { isSm, isXs } = useMatchBreakpoints()
11 | const [audioPlay, toggleSetAudioMode] = useAudioModeManager()
12 |
13 | return (
14 |
15 |
16 | {translateString(999, 'Audio')}
17 |
18 |
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | export default AudioSetting
26 |
--------------------------------------------------------------------------------
/src/components/PageHeader/SettingsModal.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Modal } from '@pancakeswap-libs/uikit'
3 | import SlippageToleranceSetting from './SlippageToleranceSetting'
4 | import TransactionDeadlineSetting from './TransactionDeadlineSetting'
5 | import AudioSetting from './AudioSetting'
6 |
7 | type SettingsModalProps = {
8 | onDismiss?: () => void
9 | translateString: (translationId: number, fallback: string) => string
10 | }
11 |
12 | // TODO: Fix UI Kit typings
13 | const defaultOnDismiss = () => null
14 |
15 | const SettingsModal = ({ onDismiss = defaultOnDismiss, translateString }: SettingsModalProps) => {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | export default SettingsModal
26 |
--------------------------------------------------------------------------------
/src/components/PageHeader/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactNode } from 'react'
2 | import styled from 'styled-components'
3 | import { Heading, IconButton, Text, Flex, useModal, TuneIcon, HistoryIcon } from '@pancakeswap-libs/uikit'
4 | import useI18n from 'hooks/useI18n'
5 | import SettingsModal from './SettingsModal'
6 | import RecentTransactionsModal from './RecentTransactionsModal'
7 |
8 | interface PageHeaderProps {
9 | title: ReactNode
10 | description?: ReactNode
11 | children?: ReactNode
12 | }
13 |
14 | const StyledPageHeader = styled.div`
15 | border-bottom: 1px solid ${({ theme }) => theme.colors.borderColor};
16 | padding: 24px;
17 | `
18 |
19 | const Details = styled.div`
20 | flex: 1;
21 | `
22 |
23 | const PageHeader = ({ title, description, children }: PageHeaderProps) => {
24 | const TranslateString = useI18n()
25 | const [onPresentSettings] = useModal()
26 | const [onPresentRecentTransactions] = useModal()
27 |
28 | return (
29 |
30 |
31 |
32 | {title}
33 | {description && (
34 |
35 | {description}
36 |
37 | )}
38 |
39 |
40 |
41 |
42 |
47 |
48 |
49 |
50 | {children && {children}}
51 |
52 | )
53 | }
54 |
55 | export default PageHeader
56 |
--------------------------------------------------------------------------------
/src/components/Pane/index.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const Pane = styled.div`
4 | border: 2px solid ${({ theme }) => theme.colors.borderColor};
5 | border-radius: 16px;
6 | padding: 16px;
7 | `
8 |
9 | export default Pane
10 |
--------------------------------------------------------------------------------
/src/components/Popups/TransactionPopup.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { AlertCircle, CheckCircle } from 'react-feather'
3 | import { Text } from '@pancakeswap-libs/uikit'
4 | import styled, { ThemeContext } from 'styled-components'
5 | import { useActiveWeb3React } from '../../hooks'
6 | import { getBscScanLink } from '../../utils'
7 | import { ExternalLink } from '../Shared'
8 | import { AutoColumn } from '../Column'
9 | import { AutoRow } from '../Row'
10 |
11 | const RowNoFlex = styled(AutoRow)`
12 | flex-wrap: nowrap;
13 | `
14 |
15 | export default function TransactionPopup({
16 | hash,
17 | success,
18 | summary,
19 | }: {
20 | hash: string
21 | success?: boolean
22 | summary?: string
23 | }) {
24 | const { chainId } = useActiveWeb3React()
25 |
26 | const theme = useContext(ThemeContext)
27 |
28 | return (
29 |
30 |
31 | {success ? (
32 |
33 | ) : (
34 |
35 | )}
36 |
37 |
38 | {summary ?? `Hash: ${hash.slice(0, 8)}...${hash.slice(58, 65)}`}
39 | {chainId && View on bscscan}
40 |
41 |
42 | )
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/Popups/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { useActivePopups } from '../../state/application/hooks'
4 | import { AutoColumn } from '../Column'
5 | import PopupItem from './PopupItem'
6 |
7 | const MobilePopupWrapper = styled.div<{ height: string | number }>`
8 | position: relative;
9 | max-width: 100%;
10 | height: ${({ height }) => height};
11 | margin: ${({ height }) => (height ? '0 auto;' : 0)};
12 | margin-bottom: ${({ height }) => (height ? '20px' : 0)}};
13 | display: none;
14 |
15 | ${({ theme }) => theme.mediaQueries.sm} {
16 | display: block;
17 | }
18 | `
19 |
20 | const MobilePopupInner = styled.div`
21 | height: 99%;
22 | overflow-x: auto;
23 | overflow-y: hidden;
24 | display: flex;
25 | flex-direction: row;
26 | -webkit-overflow-scrolling: touch;
27 | ::-webkit-scrollbar {
28 | display: none;
29 | }
30 | `
31 |
32 | const FixedPopupColumn = styled(AutoColumn)`
33 | position: fixed;
34 | top: 64px;
35 | right: 1rem;
36 | max-width: 355px !important;
37 | width: 100%;
38 | z-index: 2;
39 |
40 | ${({ theme }) => theme.mediaQueries.sm} {
41 | display: none;
42 | }
43 | `
44 |
45 | export default function Popups() {
46 | // get all popups
47 | const activePopups = useActivePopups()
48 |
49 | return (
50 | <>
51 |
52 | {activePopups.map((item) => (
53 |
54 | ))}
55 |
56 | 0 ? 'fit-content' : 0}>
57 |
58 | {activePopups // reverse so new items up front
59 | .slice(0)
60 | .reverse()
61 | .map((item) => (
62 |
63 | ))}
64 |
65 |
66 | >
67 | )
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/QuestionHelper/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from 'react'
2 | import { HelpCircle as Question } from 'react-feather'
3 | import styled from 'styled-components'
4 | import Tooltip from '../Tooltip'
5 |
6 | const QuestionWrapper = styled.div`
7 | display: flex;
8 | align-items: center;
9 | justify-content: center;
10 | padding: 0.2rem;
11 | border: none;
12 | background: none;
13 | outline: none;
14 | cursor: default;
15 | border-radius: 36px;
16 | background-color: ${({ theme }) => theme.colors.invertedContrast};
17 | color: ${({ theme }) => theme.colors.textSubtle};
18 |
19 | :hover,
20 | :focus {
21 | opacity: 0.7;
22 | }
23 | `
24 |
25 | export default function QuestionHelper({ text }: { text: string }) {
26 | const [show, setShow] = useState(false)
27 |
28 | const open = useCallback(() => setShow(true), [setShow])
29 | const close = useCallback(() => setShow(false), [setShow])
30 |
31 | return (
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/Row/index.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { Box } from 'rebass/styled-components'
3 |
4 | const Row = styled(Box)<{ align?: string; padding?: string; border?: string; borderRadius?: string }>`
5 | width: 100%;
6 | display: flex;
7 | padding: 0;
8 | align-items: ${({ align }) => (align || 'center')};
9 | padding: ${({ padding }) => padding};
10 | border: ${({ border }) => border};
11 | border-radius: ${({ borderRadius }) => borderRadius};
12 | `
13 |
14 | export const RowBetween = styled(Row)`
15 | justify-content: space-between;
16 | `
17 |
18 | export const RowFlat = styled.div`
19 | display: flex;
20 | align-items: flex-end;
21 | `
22 |
23 | export const AutoRow = styled(Row)<{ gap?: string; justify?: string }>`
24 | flex-wrap: wrap;
25 | margin: ${({ gap }) => gap && `-${gap}`};
26 | justify-content: ${({ justify }) => justify && justify};
27 |
28 | & > * {
29 | margin: ${({ gap }) => gap} !important;
30 | }
31 | `
32 |
33 | export const RowFixed = styled(Row)<{ gap?: string; justify?: string }>`
34 | width: fit-content;
35 | margin: ${({ gap }) => gap && `-${gap}`};
36 | `
37 |
38 | export default Row
39 |
--------------------------------------------------------------------------------
/src/components/SearchModal/SortButton.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Text } from '@pancakeswap-libs/uikit'
3 | import styled from 'styled-components'
4 | import { RowFixed } from '../Row'
5 |
6 | export const FilterWrapper = styled(RowFixed)`
7 | padding: 8px;
8 | background-color: ${({ theme }) => theme.colors.invertedContrast};
9 | color: ${({ theme }) => theme.colors.text};
10 | border-radius: 8px;
11 | user-select: none;
12 | & > * {
13 | user-select: none;
14 | }
15 | :hover {
16 | cursor: pointer;
17 | }
18 | `
19 |
20 | export default function SortButton({
21 | toggleSortOrder,
22 | ascending
23 | }: {
24 | toggleSortOrder: () => void
25 | ascending: boolean
26 | }) {
27 | return (
28 |
29 | {ascending ? '↑' : '↓'}
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/SearchModal/filtering.ts:
--------------------------------------------------------------------------------
1 | import { Token } from '@pancakeswap-libs/sdk'
2 | import { isAddress } from '../../utils'
3 |
4 | export function filterTokens(tokens: Token[], search: string): Token[] {
5 | if (search.length === 0) return tokens
6 |
7 | const searchingAddress = isAddress(search)
8 |
9 | if (searchingAddress) {
10 | return tokens.filter((token) => token.address === searchingAddress)
11 | }
12 |
13 | const lowerSearchParts = search
14 | .toLowerCase()
15 | .split(/\s+/)
16 | .filter((s) => s.length > 0)
17 |
18 | if (lowerSearchParts.length === 0) {
19 | return tokens
20 | }
21 |
22 | const matchesSearch = (s: string): boolean => {
23 | const sParts = s
24 | .toLowerCase()
25 | .split(/\s+/)
26 | .filter((str) => str.length > 0)
27 |
28 | return lowerSearchParts.every((p) => p.length === 0 || sParts.some((sp) => sp.startsWith(p) || sp.endsWith(p)))
29 | }
30 |
31 | return tokens.filter((token) => {
32 | const { symbol, name } = token
33 |
34 | return (symbol && matchesSearch(symbol)) || (name && matchesSearch(name))
35 | })
36 | }
37 |
38 | export default filterTokens
39 |
--------------------------------------------------------------------------------
/src/components/SearchModal/sorting.ts:
--------------------------------------------------------------------------------
1 | import { Token, TokenAmount } from '@pancakeswap-libs/sdk'
2 | import { useMemo } from 'react'
3 | import { useAllTokenBalances } from '../../state/wallet/hooks'
4 |
5 | // compare two token amounts with highest one coming first
6 | function balanceComparator(balanceA?: TokenAmount, balanceB?: TokenAmount) {
7 | if (balanceA && balanceB) {
8 | return balanceA.greaterThan(balanceB) ? -1 : balanceA.equalTo(balanceB) ? 0 : 1
9 | }
10 | if (balanceA && balanceA.greaterThan('0')) {
11 | return -1
12 | }
13 | if (balanceB && balanceB.greaterThan('0')) {
14 | return 1
15 | }
16 | return 0
17 | }
18 |
19 | function getTokenComparator(balances: {
20 | [tokenAddress: string]: TokenAmount | undefined
21 | }): (tokenA: Token, tokenB: Token) => number {
22 | return function sortTokens(tokenA: Token, tokenB: Token): number {
23 | // -1 = a is first
24 | // 1 = b is first
25 |
26 | // sort by balances
27 | const balanceA = balances[tokenA.address]
28 | const balanceB = balances[tokenB.address]
29 |
30 | const balanceComp = balanceComparator(balanceA, balanceB)
31 | if (balanceComp !== 0) return balanceComp
32 |
33 | if (tokenA.symbol && tokenB.symbol) {
34 | // sort by symbol
35 | return tokenA.symbol.toLowerCase() < tokenB.symbol.toLowerCase() ? -1 : 1
36 | }
37 | return tokenA.symbol ? -1 : tokenB.symbol ? -1 : 0
38 | }
39 | }
40 |
41 | export function useTokenComparator(inverted: boolean): (tokenA: Token, tokenB: Token) => number {
42 | const balances = useAllTokenBalances()
43 | const comparator = useMemo(() => getTokenComparator(balances ?? {}), [balances])
44 | return useMemo(() => {
45 | if (inverted) {
46 | return (tokenA: Token, tokenB: Token) => comparator(tokenA, tokenB) * -1
47 | }
48 | return comparator
49 | }, [inverted, comparator])
50 | }
51 |
52 | export default useTokenComparator
53 |
--------------------------------------------------------------------------------
/src/components/SearchModal/styleds.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { AutoColumn } from '../Column'
3 | import { RowBetween, RowFixed } from '../Row'
4 |
5 | export const FadedSpan = styled(RowFixed)`
6 | color: ${({ theme }) => theme.colors.primary};
7 | font-size: 14px;
8 | `
9 |
10 | export const PaddedColumn = styled(AutoColumn)`
11 | padding: 20px;
12 | padding-bottom: 12px;
13 | `
14 |
15 | export const MenuItem = styled(RowBetween)`
16 | padding: 4px 20px;
17 | height: 56px;
18 | display: grid;
19 | grid-template-columns: auto minmax(auto, 1fr) auto minmax(0, 72px);
20 | grid-gap: 16px;
21 | cursor: ${({ disabled }) => !disabled && 'pointer'};
22 | pointer-events: ${({ disabled }) => disabled && 'none'};
23 | :hover {
24 | background-color: ${({ theme, disabled }) => !disabled && theme.colors.invertedContrast};
25 | }
26 | opacity: ${({ disabled, selected }) => (disabled || selected ? 0.5 : 1)};
27 | `
28 |
29 | export const SearchInput = styled.input`
30 | position: relative;
31 | display: flex;
32 | padding: 16px;
33 | align-items: center;
34 | width: 100%;
35 | white-space: nowrap;
36 | background: none;
37 | border: none;
38 | outline: none;
39 | border-radius: 20px;
40 | color: ${({ theme }) => theme.colors.text};
41 | border-style: solid;
42 | border: 1px solid ${({ theme }) => theme.colors.tertiary};
43 | -webkit-appearance: none;
44 |
45 | font-size: 18px;
46 |
47 | ::placeholder {
48 | color: ${({ theme }) => theme.colors.textDisabled};
49 | }
50 | transition: border 100ms;
51 | :focus {
52 | border: 1px solid ${({ theme }) => theme.colors.primary};
53 | outline: none;
54 | }
55 | `
56 | export const Separator = styled.div`
57 | width: 100%;
58 | height: 1px;
59 | background-color: ${({ theme }) => theme.colors.invertedContrast};
60 | `
61 |
62 | export const SeparatorDark = styled.div`
63 | width: 100%;
64 | height: 1px;
65 | background-color: ${({ theme }) => theme.colors.tertiary};
66 | `
67 |
--------------------------------------------------------------------------------
/src/components/Shared/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './Common'
2 |
--------------------------------------------------------------------------------
/src/components/ToastListener/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useSelector } from 'react-redux'
3 | import { ToastContainer, Toast } from '@pancakeswap-libs/uikit'
4 | import useToast from 'hooks/useToast'
5 | import { AppState } from '../../state'
6 |
7 | const ToastListener = () => {
8 | const toasts: Toast[] = useSelector((state: AppState) => state.toasts.data)
9 | const { remove } = useToast()
10 |
11 | const handleRemove = (id: string) => remove(id)
12 |
13 | return
14 | }
15 |
16 | export default ToastListener
17 |
--------------------------------------------------------------------------------
/src/components/Tooltip/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from 'react'
2 | import styled from 'styled-components'
3 | import Popover, { PopoverProps } from '../Popover'
4 |
5 | const TooltipContainer = styled.div`
6 | width: 228px;
7 | padding: 0.6rem 1rem;
8 | line-height: 150%;
9 | font-weight: 400;
10 | `
11 |
12 | interface TooltipProps extends Omit {
13 | text: string
14 | }
15 |
16 | export default function Tooltip({ text, ...rest }: TooltipProps) {
17 | return {text}} {...rest} />
18 | }
19 |
20 | export function MouseoverTooltip({ children, ...rest }: Omit) {
21 | const [show, setShow] = useState(false)
22 | const open = useCallback(() => setShow(true), [setShow])
23 | const close = useCallback(() => setShow(false), [setShow])
24 | return (
25 |
26 |
27 | {children}
28 |
29 |
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/TransactionConfirmationModal/ConfirmationModalContent.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Wrapper, Section, BottomSection, ContentHeader } from './helpers'
3 |
4 | type ConfirmationModalContentProps = {
5 | title: string
6 | onDismiss: () => void
7 | topContent: () => React.ReactNode
8 | bottomContent: () => React.ReactNode
9 | }
10 |
11 | const ConfirmationModalContent = ({ title, bottomContent, onDismiss, topContent }: ConfirmationModalContentProps) => {
12 | return (
13 |
14 |
15 | {title}
16 | {topContent()}
17 |
18 | {bottomContent()}
19 |
20 | )
21 | }
22 |
23 | export default ConfirmationModalContent
24 |
--------------------------------------------------------------------------------
/src/components/TransactionConfirmationModal/ConfirmationPendingContent.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { Text } from '@pancakeswap-libs/uikit'
4 | import { Spinner } from '../Shared'
5 | import { AutoColumn } from '../Column'
6 | import { Wrapper, Section, ConfirmedIcon, ContentHeader } from './helpers'
7 |
8 | type ConfirmationPendingContentProps = { onDismiss: () => void; pendingText: string }
9 |
10 | const CustomLightSpinner = styled(Spinner)<{ size: string }>`
11 | height: ${({ size }) => size};
12 | width: ${({ size }) => size};
13 | `
14 |
15 | const ConfirmationPendingContent = ({ onDismiss, pendingText }: ConfirmationPendingContentProps) => {
16 | return (
17 |
18 |
19 | Waiting for confirmation
20 |
21 |
22 |
23 |
24 |
25 |
26 | {pendingText}
27 |
28 |
29 | Confirm this transaction in your wallet
30 |
31 |
32 |
33 | )
34 | }
35 |
36 | export default ConfirmationPendingContent
37 |
--------------------------------------------------------------------------------
/src/components/TransactionConfirmationModal/TransactionConfirmationModal.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Modal from '../Modal'
3 | import { useActiveWeb3React } from '../../hooks'
4 | import ConfirmationPendingContent from './ConfirmationPendingContent'
5 | import TransactionSubmittedContent from './TransactionSubmittedContent'
6 |
7 | interface ConfirmationModalProps {
8 | isOpen: boolean
9 | onDismiss: () => void
10 | hash: string | undefined
11 | content: () => React.ReactNode
12 | attemptingTxn: boolean
13 | pendingText: string
14 | }
15 |
16 | const TransactionConfirmationModal = ({
17 | isOpen,
18 | onDismiss,
19 | attemptingTxn,
20 | hash,
21 | pendingText,
22 | content
23 | }: ConfirmationModalProps) => {
24 | const { chainId } = useActiveWeb3React()
25 |
26 | if (!chainId) return null
27 |
28 | // confirmation screen
29 | return (
30 |
31 | {attemptingTxn ? (
32 |
33 | ) : hash ? (
34 |
35 | ) : (
36 | content()
37 | )}
38 |
39 | )
40 | }
41 |
42 | export default TransactionConfirmationModal
43 |
--------------------------------------------------------------------------------
/src/components/TransactionConfirmationModal/TransactionErrorContent.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { ThemeContext } from 'styled-components'
3 | import { Button, Text } from '@pancakeswap-libs/uikit'
4 | import { AlertTriangle } from 'react-feather'
5 | import { AutoColumn } from '../Column'
6 | import { Wrapper, Section, BottomSection, ContentHeader } from './helpers'
7 |
8 | type TransactionErrorContentProps = { message: string; onDismiss: () => void }
9 |
10 | const TransactionErrorContent = ({ message, onDismiss }: TransactionErrorContentProps) => {
11 | const theme = useContext(ThemeContext)
12 | return (
13 |
14 |
15 | Error
16 |
17 |
18 |
19 | {message}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | )
28 | }
29 |
30 | export default TransactionErrorContent
31 |
--------------------------------------------------------------------------------
/src/components/TransactionConfirmationModal/TransactionSubmittedContent.tsx:
--------------------------------------------------------------------------------
1 | import { ChainId } from '@pancakeswap-libs/sdk'
2 | import React, { useContext } from 'react'
3 | import { ThemeContext } from 'styled-components'
4 | import { Button, LinkExternal } from '@pancakeswap-libs/uikit'
5 | import { ArrowUpCircle } from 'react-feather'
6 | import { AutoColumn } from '../Column'
7 | import { getBscScanLink } from '../../utils'
8 | import { Wrapper, Section, ConfirmedIcon, ContentHeader } from './helpers'
9 |
10 | type TransactionSubmittedContentProps = {
11 | onDismiss: () => void
12 | hash: string | undefined
13 | chainId: ChainId
14 | }
15 |
16 | const TransactionSubmittedContent = ({ onDismiss, chainId, hash }: TransactionSubmittedContentProps) => {
17 | const theme = useContext(ThemeContext)
18 |
19 | return (
20 |
21 |
22 | Transaction submitted
23 |
24 |
25 |
26 |
27 | {chainId && hash && (
28 | View on BscScan
29 | )}
30 |
33 |
34 |
35 |
36 | )
37 | }
38 |
39 | export default TransactionSubmittedContent
40 |
--------------------------------------------------------------------------------
/src/components/TransactionConfirmationModal/helpers.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactNode } from 'react'
2 | import styled from 'styled-components'
3 | import { Heading, IconButton, CloseIcon } from '@pancakeswap-libs/uikit'
4 | import { AutoColumn, ColumnCenter } from '../Column'
5 |
6 |
7 | export const Wrapper = styled.div`
8 | width: 100%;
9 | overflow-y: auto;
10 | `
11 | export const Section = styled(AutoColumn)`
12 | padding: 24px;
13 | `
14 |
15 | export const ConfirmedIcon = styled(ColumnCenter)`
16 | padding: 40px 0;
17 | `
18 |
19 | export const BottomSection = styled(Section)`
20 | background-color: ${({ theme }) => theme.colors.invertedContrast};
21 | border-bottom-left-radius: 20px;
22 | border-bottom-right-radius: 20px;
23 | `
24 |
25 | /**
26 | * TODO: Remove this when modal system from the UI Kit is implemented
27 | */
28 | const StyledContentHeader = styled.div`
29 | align-items: center;
30 | display: flex;
31 |
32 | & > ${Heading} {
33 | flex: 1;
34 | }
35 | `
36 |
37 | type ContentHeaderProps = {
38 | children: ReactNode
39 | onDismiss: () => void
40 | }
41 |
42 | export const ContentHeader = ({ children, onDismiss }: ContentHeaderProps) => (
43 |
44 | {children}
45 |
46 |
47 |
48 |
49 | )
50 |
--------------------------------------------------------------------------------
/src/components/TransactionConfirmationModal/index.tsx:
--------------------------------------------------------------------------------
1 | import TransactionConfirmationModal from './TransactionConfirmationModal'
2 |
3 | export { default as ConfirmationModalContent } from './ConfirmationModalContent'
4 | export { default as ConfirmationPendingContent } from './ConfirmationPendingContent'
5 | export { default as TransactionErrorContent } from './TransactionErrorContent'
6 | export { default as TransactionSubmittedContent } from './TransactionSubmittedContent'
7 |
8 | export default TransactionConfirmationModal
9 |
--------------------------------------------------------------------------------
/src/components/pancake/CoinLogo.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import Logo from '../Logo'
3 |
4 | const CoinLogo = styled(Logo)<{ size: string }>`
5 | width: ${({ size }) => size};
6 | height: ${({ size }) => size};
7 | `
8 |
9 | export default CoinLogo
10 |
--------------------------------------------------------------------------------
/src/components/swap/AdvancedSwapDetailsDropdown.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { useLastTruthy } from '../../hooks/useLast'
4 | import { AdvancedSwapDetails, AdvancedSwapDetailsProps } from './AdvancedSwapDetails'
5 |
6 | const AdvancedDetailsFooter = styled.div<{ show: boolean }>`
7 | padding-top: calc(16px + 2rem);
8 | padding-bottom: 20px;
9 | margin-top: -2rem;
10 | width: 100%;
11 | max-width: 400px;
12 | border-bottom-left-radius: 20px;
13 | border-bottom-right-radius: 20px;
14 | color: ${({ theme }) => theme.colors.textSubtle};
15 | z-index: 1;
16 |
17 | transform: ${({ show }) => (show ? 'translateY(0%)' : 'translateY(-100%)')};
18 | transition: transform 300ms ease-in-out;
19 | `
20 |
21 | export default function AdvancedSwapDetailsDropdown({ trade, ...rest }: AdvancedSwapDetailsProps) {
22 | const lastTrade = useLastTruthy(trade)
23 |
24 | return (
25 |
26 |
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/swap/FormattedPriceImpact.tsx:
--------------------------------------------------------------------------------
1 | import { Percent } from '@pancakeswap-libs/sdk'
2 | import React from 'react'
3 | import { ONE_BIPS } from '../../constants'
4 | import { warningSeverity } from '../../utils/prices'
5 | import { ErrorText } from './styleds'
6 |
7 | /**
8 | * Formatted version of price impact text with warning colors
9 | */
10 | export default function FormattedPriceImpact({ priceImpact }: { priceImpact?: Percent }) {
11 | return (
12 |
13 | {priceImpact ? (priceImpact.lessThan(ONE_BIPS) ? '<0.01%' : `${priceImpact.toFixed(2)}%`) : '-'}
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/swap/SwapRoute.tsx:
--------------------------------------------------------------------------------
1 | import { Trade } from '@pancakeswap-libs/sdk'
2 | import React, { Fragment, memo, useContext } from 'react'
3 | import { ChevronRight } from 'react-feather'
4 | import { Flex, Text } from '@pancakeswap-libs/uikit'
5 | import { ThemeContext } from 'styled-components'
6 | import CurrencyLogo from '../CurrencyLogo'
7 |
8 | export default memo(function SwapRoute({ trade }: { trade: Trade }) {
9 | const theme = useContext(ThemeContext)
10 | return (
11 |
20 | {trade.route.path.map((token, i, path) => {
21 | const isLastItem: boolean = i === path.length - 1
22 | return (
23 | // eslint-disable-next-line react/no-array-index-key
24 |
25 |
26 |
27 |
28 | {token.symbol}
29 |
30 |
31 | {isLastItem ? null : }
32 |
33 | )
34 | })}
35 |
36 | )
37 | })
38 |
--------------------------------------------------------------------------------
/src/components/swap/TradePrice.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Price } from '@pancakeswap-libs/sdk'
3 | import { SyncAltIcon, Text } from '@pancakeswap-libs/uikit'
4 | import { StyledBalanceMaxMini } from './styleds'
5 |
6 | interface TradePriceProps {
7 | price?: Price
8 | showInverted: boolean
9 | setShowInverted: (showInverted: boolean) => void
10 | }
11 |
12 | export default function TradePrice({ price, showInverted, setShowInverted }: TradePriceProps) {
13 | const formattedPrice = showInverted ? price?.toSignificant(6) : price?.invert()?.toSignificant(6)
14 |
15 | const show = Boolean(price?.baseCurrency && price?.quoteCurrency)
16 | const label = showInverted
17 | ? `${price?.quoteCurrency?.symbol} per ${price?.baseCurrency?.symbol}`
18 | : `${price?.baseCurrency?.symbol} per ${price?.quoteCurrency?.symbol}`
19 |
20 | return (
21 |
22 | {show ? (
23 | <>
24 | {formattedPrice ?? '-'} {label}
25 | setShowInverted(!showInverted)}>
26 |
27 |
28 | >
29 | ) : (
30 | '-'
31 | )}
32 |
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/swap/confirmPriceImpactWithoutFee.ts:
--------------------------------------------------------------------------------
1 | import { Percent } from '@pancakeswap-libs/sdk'
2 | import { ALLOWED_PRICE_IMPACT_HIGH, PRICE_IMPACT_WITHOUT_FEE_CONFIRM_MIN } from '../../constants'
3 |
4 | /**
5 | * Given the price impact, get user confirmation.
6 | *
7 | * @param priceImpactWithoutFee price impact of the trade without the fee.
8 | */
9 | export default function confirmPriceImpactWithoutFee(priceImpactWithoutFee: Percent): boolean {
10 | if (!priceImpactWithoutFee.lessThan(PRICE_IMPACT_WITHOUT_FEE_CONFIRM_MIN)) {
11 | return (
12 | window.prompt(
13 | `This swap has a price impact of at least ${PRICE_IMPACT_WITHOUT_FEE_CONFIRM_MIN.toFixed(
14 | 0
15 | )}%. Please type the word "confirm" to continue with this swap.`
16 | ) === 'confirm'
17 | )
18 | } if (!priceImpactWithoutFee.lessThan(ALLOWED_PRICE_IMPACT_HIGH)) {
19 | return window.confirm(
20 | `This swap has a price impact of at least ${ALLOWED_PRICE_IMPACT_HIGH.toFixed(
21 | 0
22 | )}%. Please confirm that you would like to continue with this swap.`
23 | )
24 | }
25 | return true
26 | }
27 |
--------------------------------------------------------------------------------
/src/connectors/index.ts:
--------------------------------------------------------------------------------
1 | import { ConnectorNames } from '@pancakeswap-libs/uikit'
2 | import { Web3Provider } from '@ethersproject/providers'
3 | import { InjectedConnector } from '@web3-react/injected-connector'
4 | import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
5 | import { WalletLinkConnector } from '@web3-react/walletlink-connector'
6 | import { BscConnector } from '@binance-chain/bsc-connector'
7 | import { NetworkConnector } from './NetworkConnector'
8 |
9 | const NETWORK_URL = process.env.REACT_APP_NETWORK_URL
10 |
11 | export const NETWORK_CHAIN_ID: number = parseInt(process.env.REACT_APP_CHAIN_ID ?? '56')
12 |
13 | if (typeof NETWORK_URL === 'undefined') {
14 | throw new Error(`REACT_APP_NETWORK_URL must be a defined environment variable`)
15 | }
16 |
17 | export const network = new NetworkConnector({
18 | urls: { [NETWORK_CHAIN_ID]: NETWORK_URL },
19 | })
20 |
21 | let networkLibrary: Web3Provider | undefined
22 | export function getNetworkLibrary(): Web3Provider {
23 | // eslint-disable-next-line no-return-assign
24 | return (networkLibrary = networkLibrary ?? new Web3Provider(network.provider as any))
25 | }
26 |
27 | export const injected = new InjectedConnector({
28 | supportedChainIds: [56, 97],
29 | })
30 |
31 | export const bscConnector = new BscConnector({ supportedChainIds: [56] })
32 |
33 | // mainnet only
34 | export const walletconnect = new WalletConnectConnector({
35 | rpc: { [NETWORK_CHAIN_ID]: NETWORK_URL },
36 | bridge: 'https://bridge.walletconnect.org',
37 | qrcode: true,
38 | pollingInterval: 15000,
39 | })
40 |
41 | // mainnet only
42 | export const walletlink = new WalletLinkConnector({
43 | url: NETWORK_URL,
44 | appName: 'Uniswap',
45 | appLogoUrl:
46 | 'https://mpng.pngfly.com/20181202/bex/kisspng-emoji-domain-unicorn-pin-badges-sticker-unicorn-tumblr-emoji-unicorn-iphoneemoji-5c046729264a77.5671679315437924251569.jpg',
47 | })
48 |
49 | export const connectorsByName: { [connectorName in ConnectorNames]: any } = {
50 | [ConnectorNames.Injected]: injected,
51 | [ConnectorNames.WalletConnect]: walletconnect,
52 | [ConnectorNames.BSC]: bscConnector,
53 | }
54 |
--------------------------------------------------------------------------------
/src/constants/abis/erc20.ts:
--------------------------------------------------------------------------------
1 | import { Interface } from '@ethersproject/abi'
2 | import ERC20_ABI from './erc20.json'
3 | import ERC20_BYTES32_ABI from './erc20_bytes32.json'
4 |
5 | const ERC20_INTERFACE = new Interface(ERC20_ABI)
6 |
7 | export default ERC20_INTERFACE
8 | export { ERC20_ABI, ERC20_BYTES32_ABI }
9 |
--------------------------------------------------------------------------------
/src/constants/abis/erc20_bytes32.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "constant": true,
4 | "inputs": [],
5 | "name": "name",
6 | "outputs": [
7 | {
8 | "name": "",
9 | "type": "bytes32"
10 | }
11 | ],
12 | "payable": false,
13 | "stateMutability": "view",
14 | "type": "function"
15 | },
16 | {
17 | "constant": true,
18 | "inputs": [],
19 | "name": "symbol",
20 | "outputs": [
21 | {
22 | "name": "",
23 | "type": "bytes32"
24 | }
25 | ],
26 | "payable": false,
27 | "stateMutability": "view",
28 | "type": "function"
29 | }
30 | ]
31 |
--------------------------------------------------------------------------------
/src/constants/lists.ts:
--------------------------------------------------------------------------------
1 | export const DEFAULT_TOKEN_LIST_URL = 'pancakeswap'
2 |
3 | export const DEFAULT_LIST_OF_LISTS: string[] = [DEFAULT_TOKEN_LIST_URL]
4 |
--------------------------------------------------------------------------------
/src/constants/localisation/languageCodes.ts:
--------------------------------------------------------------------------------
1 | export const EN = { code: 'en', language: 'English' }
2 | export const AR = { code: 'ar', language: 'العربية' }
3 | export const BN = { code: 'bn', language: 'বাংলা'}
4 | export const ZHCN = { code: 'zh-CN', language: '简体中文' }
5 | export const ZHTW = { code: 'zh-TW', language: '繁體中文' }
6 | export const NL = { code: 'nl', language: 'Nederlands' }
7 | export const FIL = { code: 'fil', language: 'Filipino' }
8 | export const FI = { code: 'fi', language: 'Suomalainen' }
9 | export const FR = { code: 'fr', language: 'Français' }
10 | export const DE = { code: 'de', language: 'Deutsch' }
11 | export const EL = { code: 'el', language: 'Ελληνικά' }
12 | export const HI = { code: 'hi', language: 'हिंदी' }
13 | export const HU = { code: 'hu', language: 'Magyar' }
14 | export const ID = { code: 'id', language: 'Bahasa Indonesia' }
15 | export const IT = { code: 'it', language: 'Italiano' }
16 | export const JA = { code: 'ja', language: '日本語' }
17 | export const KO = { code: 'ko', language: '한국어' }
18 | export const PL = { code: 'pl', language: 'Polski' }
19 | export const PTPT = { code: 'pt-PT', language: 'Português (Portugal)' }
20 | export const PTBR = { code: 'pt-BR', language: 'Português (Brasil)' }
21 | export const RO = { code: 'ro', language: 'Română' }
22 | export const RU = { code: 'ru', language: 'Русский' }
23 | export const ESES = { code: 'es-ES', language: 'Español' }
24 | export const SVSE = { code: 'sv-SE', language: 'Svenska' }
25 | export const TA = { code: 'ta', language: 'தமிழ்' }
26 | export const TR = { code: 'tr', language: 'Türkçe' }
27 | export const UK = { code: 'uk', language: 'Українська' }
28 | export const VI = { code: 'vi', language: 'Tiếng Việt' }
29 |
30 | export const allLanguages = [
31 | EN,
32 | AR,
33 | BN,
34 | ZHCN,
35 | ZHTW,
36 | NL,
37 | FIL,
38 | FI,
39 | FR,
40 | DE,
41 | EL,
42 | HI,
43 | HU,
44 | ID,
45 | IT,
46 | JA,
47 | KO,
48 | PL,
49 | PTPT,
50 | PTBR,
51 | RO,
52 | RU,
53 | ESES,
54 | SVSE,
55 | TA,
56 | TR,
57 | UK,
58 | VI
59 | ]
60 |
--------------------------------------------------------------------------------
/src/constants/multicall/index.ts:
--------------------------------------------------------------------------------
1 | import { ChainId } from '@pancakeswap-libs/sdk'
2 | import MULTICALL_ABI from './abi.json'
3 |
4 | const MULTICALL_NETWORKS: { [chainId in ChainId]: string } = {
5 | [ChainId.MAINNET]: '0x1Ee38d535d541c55C9dae27B12edf090C608E6Fb', // TODO
6 | [ChainId.BSCTESTNET]: '0x301907b5835a2d723Fe3e9E8C5Bc5375d5c1236A'
7 | }
8 |
9 | export { MULTICALL_ABI, MULTICALL_NETWORKS }
10 |
--------------------------------------------------------------------------------
/src/data/Allowances.ts:
--------------------------------------------------------------------------------
1 | import { Token, TokenAmount } from '@pancakeswap-libs/sdk'
2 | import { useMemo } from 'react'
3 |
4 | import { useTokenContract } from '../hooks/useContract'
5 | import { useSingleCallResult } from '../state/multicall/hooks'
6 |
7 | export function useTokenAllowance(token?: Token, owner?: string, spender?: string): TokenAmount | undefined {
8 | const contract = useTokenContract(token?.address, false)
9 |
10 | const inputs = useMemo(() => [owner, spender], [owner, spender])
11 | const allowance = useSingleCallResult(contract, 'allowance', inputs).result
12 |
13 | return useMemo(() => (token && allowance ? new TokenAmount(token, allowance.toString()) : undefined), [
14 | token,
15 | allowance,
16 | ])
17 | }
18 |
19 | export default useTokenAllowance
20 |
--------------------------------------------------------------------------------
/src/data/TotalSupply.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber } from '@ethersproject/bignumber'
2 | import { Token, TokenAmount } from '@pancakeswap-libs/sdk'
3 | import { useTokenContract } from '../hooks/useContract'
4 | import { useSingleCallResult } from '../state/multicall/hooks'
5 |
6 | // returns undefined if input token is undefined, or fails to get token contract,
7 | // or contract total supply cannot be fetched
8 | export function useTotalSupply(token?: Token): TokenAmount | undefined {
9 | const contract = useTokenContract(token?.address, false)
10 |
11 | const totalSupply: BigNumber = useSingleCallResult(contract, 'totalSupply')?.result?.[0]
12 |
13 | return token && totalSupply ? new TokenAmount(token, totalSupply.toString()) : undefined
14 | }
15 |
16 | export default useTotalSupply
17 |
--------------------------------------------------------------------------------
/src/hooks/LanguageContext.ts:
--------------------------------------------------------------------------------
1 | import React, { createContext } from 'react'
2 | import { LangType } from '@pancakeswap-libs/uikit'
3 |
4 | export interface LanguageObject {
5 | code: string
6 | language: string
7 | }
8 | interface LanguageState {
9 | selectedLanguage: LanguageObject
10 | setSelectedLanguage: (langObject: LangType) => void
11 | translatedLanguage: LanguageObject
12 | setTranslatedLanguage: React.Dispatch>
13 | }
14 |
15 | const defaultLanguageState: LanguageState = {
16 | selectedLanguage: { code: '', language: '' },
17 | setSelectedLanguage: (): void => undefined,
18 | translatedLanguage: { code: '', language: '' },
19 | setTranslatedLanguage: (): void => undefined,
20 | }
21 |
22 | export const LanguageContext = createContext(defaultLanguageState as LanguageState)
23 |
--------------------------------------------------------------------------------
/src/hooks/TranslationsContext.ts:
--------------------------------------------------------------------------------
1 | import React, { createContext } from 'react'
2 |
3 | interface TranslationState {
4 | translations: Array
5 | setTranslations: React.Dispatch>>
6 | }
7 |
8 | const defaultTranslationState: TranslationState = {
9 | translations: [],
10 | setTranslations: (): void => undefined,
11 | }
12 |
13 | export const TranslationsContext = createContext(defaultTranslationState as TranslationState)
14 |
15 | export default TranslationsContext
16 |
--------------------------------------------------------------------------------
/src/hooks/useAuth.ts:
--------------------------------------------------------------------------------
1 | import { useCallback } from 'react'
2 | import { useWeb3React, UnsupportedChainIdError } from '@web3-react/core'
3 | import { NoBscProviderError } from '@binance-chain/bsc-connector'
4 | import {
5 | NoEthereumProviderError,
6 | UserRejectedRequestError as UserRejectedRequestErrorInjected,
7 | } from '@web3-react/injected-connector'
8 | import {
9 | UserRejectedRequestError as UserRejectedRequestErrorWalletConnect,
10 | WalletConnectConnector,
11 | } from '@web3-react/walletconnect-connector'
12 | import { connectorLocalStorageKey, ConnectorNames } from '@pancakeswap-libs/uikit'
13 | import useToast from 'hooks/useToast'
14 | import { connectorsByName } from 'connectors'
15 |
16 | const useAuth = () => {
17 | const { activate, deactivate } = useWeb3React()
18 | const { toastError } = useToast()
19 |
20 | const login = useCallback((connectorID: ConnectorNames) => {
21 | const connector = connectorsByName[connectorID]
22 | if (connector) {
23 | activate(connector, async (error: Error) => {
24 | window.localStorage.removeItem(connectorLocalStorageKey)
25 | if (error instanceof UnsupportedChainIdError) {
26 | toastError('Unsupported Chain Id', 'Unsupported Chain Id Error. Check your chain Id.')
27 | } else if (error instanceof NoEthereumProviderError || error instanceof NoBscProviderError) {
28 | toastError('Provider Error', 'No provider was found')
29 | } else if (
30 | error instanceof UserRejectedRequestErrorInjected ||
31 | error instanceof UserRejectedRequestErrorWalletConnect
32 | ) {
33 | if (connector instanceof WalletConnectConnector) {
34 | const walletConnector = connector as WalletConnectConnector
35 | walletConnector.walletConnectProvider = null
36 | }
37 | toastError('Authorization Error', 'Please authorize to access your account')
38 | } else {
39 | toastError(error.name, error.message)
40 | }
41 | })
42 | } else {
43 | toastError("Can't find connector", 'The connector config is wrong')
44 | }
45 | // eslint-disable-next-line react-hooks/exhaustive-deps
46 | }, [])
47 |
48 | return { login, logout: deactivate }
49 | }
50 |
51 | export default useAuth
52 |
--------------------------------------------------------------------------------
/src/hooks/useDebounce.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | // modified from https://usehooks.com/useDebounce/
4 | export default function useDebounce(value: T, delay: number): T {
5 | const [debouncedValue, setDebouncedValue] = useState(value)
6 |
7 | useEffect(() => {
8 | // Update debounced value after delay
9 | const handler = setTimeout(() => {
10 | setDebouncedValue(value)
11 | }, delay)
12 |
13 | // Cancel the timeout if value changes (also on delay change or unmount)
14 | // This is how we prevent debounced value from updating if value is changed ...
15 | // .. within the delay period. Timeout gets cleared and restarted.
16 | return () => {
17 | clearTimeout(handler)
18 | }
19 | }, [value, delay])
20 |
21 | return debouncedValue
22 | }
23 |
--------------------------------------------------------------------------------
/src/hooks/useENS.ts:
--------------------------------------------------------------------------------
1 | import { isAddress } from '../utils'
2 | import useENSAddress from './useENSAddress'
3 | import useENSName from './useENSName'
4 |
5 | /**
6 | * Given a name or address, does a lookup to resolve to an address and name
7 | * @param nameOrAddress ENS name or address
8 | */
9 | export default function useENS(
10 | nameOrAddress?: string | null
11 | ): { loading: boolean; address: string | null; name: string | null } {
12 | const validated = isAddress(nameOrAddress)
13 | const reverseLookup = useENSName(validated || undefined)
14 | const lookup = useENSAddress(nameOrAddress)
15 |
16 | return {
17 | loading: reverseLookup.loading || lookup.loading,
18 | address: validated || lookup.address,
19 | name: reverseLookup.ENSName ? reverseLookup.ENSName : !validated && lookup.address ? nameOrAddress || null : null
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/hooks/useENSAddress.ts:
--------------------------------------------------------------------------------
1 | import { namehash } from 'ethers/lib/utils'
2 | import { useMemo } from 'react'
3 | import { useSingleCallResult } from '../state/multicall/hooks'
4 | import isZero from '../utils/isZero'
5 | import { useENSRegistrarContract, useENSResolverContract } from './useContract'
6 | import useDebounce from './useDebounce'
7 |
8 | /**
9 | * Does a lookup for an ENS name to find its address.
10 | */
11 | export default function useENSAddress(ensName?: string | null): { loading: boolean; address: string | null } {
12 | const debouncedName = useDebounce(ensName, 200)
13 | const ensNodeArgument = useMemo(() => {
14 | if (!debouncedName) return [undefined]
15 | try {
16 | return debouncedName ? [namehash(debouncedName)] : [undefined]
17 | } catch (error) {
18 | return [undefined]
19 | }
20 | }, [debouncedName])
21 | const registrarContract = useENSRegistrarContract(false)
22 | const resolverAddress = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument)
23 | const resolverAddressResult = resolverAddress.result?.[0]
24 | const resolverContract = useENSResolverContract(
25 | resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined,
26 | false
27 | )
28 | const addr = useSingleCallResult(resolverContract, 'addr', ensNodeArgument)
29 |
30 | const changed = debouncedName !== ensName
31 | return {
32 | address: changed ? null : addr.result?.[0] ?? null,
33 | loading: changed || resolverAddress.loading || addr.loading
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/hooks/useENSContentHash.ts:
--------------------------------------------------------------------------------
1 | import { namehash } from 'ethers/lib/utils'
2 | import { useMemo } from 'react'
3 | import { useSingleCallResult } from '../state/multicall/hooks'
4 | import isZero from '../utils/isZero'
5 | import { useENSRegistrarContract, useENSResolverContract } from './useContract'
6 |
7 | /**
8 | * Does a lookup for an ENS name to find its contenthash.
9 | */
10 | export default function useENSContentHash(ensName?: string | null): { loading: boolean; contenthash: string | null } {
11 | const ensNodeArgument = useMemo(() => {
12 | if (!ensName) return [undefined]
13 | try {
14 | return ensName ? [namehash(ensName)] : [undefined]
15 | } catch (error) {
16 | return [undefined]
17 | }
18 | }, [ensName])
19 | const registrarContract = useENSRegistrarContract(false)
20 | const resolverAddressResult = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument)
21 | const resolverAddress = resolverAddressResult.result?.[0]
22 | const resolverContract = useENSResolverContract(
23 | resolverAddress && isZero(resolverAddress) ? undefined : resolverAddress,
24 | false
25 | )
26 | const contenthash = useSingleCallResult(resolverContract, 'contenthash', ensNodeArgument)
27 |
28 | return {
29 | contenthash: contenthash.result?.[0] ?? null,
30 | loading: resolverAddressResult.loading || contenthash.loading
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/hooks/useENSName.ts:
--------------------------------------------------------------------------------
1 | import { namehash } from 'ethers/lib/utils'
2 | import { useMemo } from 'react'
3 | import { useSingleCallResult } from '../state/multicall/hooks'
4 | import { isAddress } from '../utils'
5 | import isZero from '../utils/isZero'
6 | import { useENSRegistrarContract, useENSResolverContract } from './useContract'
7 | import useDebounce from './useDebounce'
8 |
9 | /**
10 | * Does a reverse lookup for an address to find its ENS name.
11 | * Note this is not the same as looking up an ENS name to find an address.
12 | */
13 | export default function useENSName(address?: string): { ENSName: string | null; loading: boolean } {
14 | const debouncedAddress = useDebounce(address, 200)
15 | const ensNodeArgument = useMemo(() => {
16 | if (!debouncedAddress || !isAddress(debouncedAddress)) return [undefined]
17 | try {
18 | return debouncedAddress ? [namehash(`${debouncedAddress.toLowerCase().substr(2)}.addr.reverse`)] : [undefined]
19 | } catch (error) {
20 | return [undefined]
21 | }
22 | }, [debouncedAddress])
23 | const registrarContract = useENSRegistrarContract(false)
24 | const resolverAddress = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument)
25 | const resolverAddressResult = resolverAddress.result?.[0]
26 | const resolverContract = useENSResolverContract(
27 | resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined,
28 | false
29 | )
30 | const name = useSingleCallResult(resolverContract, 'name', ensNodeArgument)
31 |
32 | const changed = debouncedAddress !== address
33 | return {
34 | ENSName: changed ? null : name.result?.[0] ?? null,
35 | loading: changed || resolverAddress.loading || name.loading
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/hooks/useFetchListCallback.ts:
--------------------------------------------------------------------------------
1 | import { nanoid } from '@reduxjs/toolkit'
2 | import { ChainId } from '@pancakeswap-libs/sdk'
3 | import { TokenList } from '@uniswap/token-lists'
4 | import { useCallback } from 'react'
5 | import { useDispatch } from 'react-redux'
6 | import { getNetworkLibrary, NETWORK_CHAIN_ID } from '../connectors'
7 | import { AppDispatch } from '../state'
8 | import { fetchTokenList } from '../state/lists/actions'
9 | import getTokenList from '../utils/getTokenList'
10 | import resolveENSContentHash from '../utils/resolveENSContentHash'
11 | import { useActiveWeb3React } from './index'
12 |
13 | export function useFetchListCallback(): (listUrl: string) => Promise {
14 | const { chainId, library } = useActiveWeb3React()
15 | const dispatch = useDispatch()
16 |
17 | const ensResolver = useCallback(
18 | (ensName: string) => {
19 | if (!library || chainId !== ChainId.MAINNET) {
20 | if (NETWORK_CHAIN_ID === ChainId.MAINNET) {
21 | const networkLibrary = getNetworkLibrary()
22 | if (networkLibrary) {
23 | return resolveENSContentHash(ensName, networkLibrary)
24 | }
25 | }
26 | throw new Error('Could not construct mainnet ENS resolver')
27 | }
28 | return resolveENSContentHash(ensName, library)
29 | },
30 | [chainId, library]
31 | )
32 |
33 | return useCallback(
34 | async (listUrl: string) => {
35 | const requestId = nanoid()
36 | dispatch(fetchTokenList.pending({ requestId, url: listUrl }))
37 | return getTokenList(listUrl, ensResolver)
38 | .then((tokenList) => {
39 | dispatch(fetchTokenList.fulfilled({ url: listUrl, tokenList, requestId }))
40 | return tokenList
41 | })
42 | .catch((error) => {
43 | console.error(`Failed to get list at url ${listUrl}`, error)
44 | dispatch(fetchTokenList.rejected({ url: listUrl, requestId, errorMessage: error.message }))
45 | throw error
46 | })
47 | },
48 | [dispatch, ensResolver]
49 | )
50 | }
51 |
52 | export default useFetchListCallback
53 |
--------------------------------------------------------------------------------
/src/hooks/useGetDocumentTitlePrice.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import useGetPriceData from './useGetPriceData'
3 | import { CAKE } from '../constants'
4 |
5 | const useGetDocumentTitlePrice = () => {
6 | const priceData = useGetPriceData()
7 |
8 | const cakePriceUsd = priceData ? parseFloat(priceData.data[CAKE.address].price) : 0
9 |
10 | const cakePriceUsdString =
11 | Number.isNaN(cakePriceUsd) || cakePriceUsd === 0
12 | ? ''
13 | : ` - $${cakePriceUsd.toLocaleString(undefined, {
14 | minimumFractionDigits: 3,
15 | maximumFractionDigits: 3,
16 | })}`
17 |
18 | useEffect(() => {
19 | document.title = `PancakeSwap${cakePriceUsdString}`
20 | }, [cakePriceUsdString])
21 | }
22 | export default useGetDocumentTitlePrice
23 |
--------------------------------------------------------------------------------
/src/hooks/useGetLocalProfile.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import Cookies from 'js-cookie'
3 | import { useActiveWeb3React } from 'hooks'
4 |
5 | const initialState = {
6 | profileLink: 'https://pancakeswap.finance/profile',
7 | noProfileLink: 'https://pancakeswap.finance/profile',
8 | }
9 |
10 | /**
11 | * Note - this will only work if the app is on the same domain
12 | */
13 | const useGetLocalProfile = () => {
14 | const [profile, setProfile] = useState(initialState)
15 | const { account } = useActiveWeb3React()
16 |
17 | useEffect(() => {
18 | if (account) {
19 | try {
20 | const localData = Cookies.get(`profile_${account}`)
21 |
22 | if (localData) {
23 | const localProfile = JSON.parse(localData)
24 |
25 | setProfile((prevProfile) => ({
26 | ...prevProfile,
27 | username: localProfile.username,
28 | image: localProfile.avatar,
29 | }))
30 | }
31 | } catch (error) {
32 | setProfile(initialState)
33 | }
34 | } else {
35 | setProfile(initialState)
36 | }
37 | }, [account, setProfile])
38 |
39 | return profile
40 | }
41 |
42 | export default useGetLocalProfile
43 |
--------------------------------------------------------------------------------
/src/hooks/useGetPriceData.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | type ApiResponse = {
4 | updated_at: string
5 | data: {
6 | [key: string]: {
7 | name: string
8 | symbol: string
9 | price: string
10 | price_BNB: string
11 | }
12 | }
13 | }
14 |
15 | const api = 'https://api.pancakeswap.info/api/tokens'
16 |
17 | const useGetPriceData = () => {
18 | const [data, setData] = useState(null)
19 |
20 | useEffect(() => {
21 | const fetchData = async () => {
22 | try {
23 | const response = await fetch(api)
24 | const res: ApiResponse = await response.json()
25 |
26 | setData(res)
27 | } catch (error) {
28 | console.error('Unable to fetch price data:', error)
29 | }
30 | }
31 |
32 | fetchData()
33 | }, [setData])
34 |
35 | return data
36 | }
37 |
38 | export default useGetPriceData
39 |
--------------------------------------------------------------------------------
/src/hooks/useHttpLocations.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import contenthashToUri from '../utils/contenthashToUri'
3 | import { parseENSAddress } from '../utils/parseENSAddress'
4 | import uriToHttp from '../utils/uriToHttp'
5 | import useENSContentHash from './useENSContentHash'
6 |
7 | export default function useHttpLocations(uri: string | undefined): string[] {
8 | const ens = useMemo(() => (uri ? parseENSAddress(uri) : undefined), [uri])
9 | const resolvedContentHash = useENSContentHash(ens?.ensName)
10 | return useMemo(() => {
11 | if (ens) {
12 | return resolvedContentHash.contenthash ? uriToHttp(contenthashToUri(resolvedContentHash.contenthash)) : []
13 | }
14 | return uri ? uriToHttp(uri) : []
15 |
16 | }, [ens, resolvedContentHash.contenthash, uri])
17 | }
18 |
--------------------------------------------------------------------------------
/src/hooks/useI18n.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useContext } from 'react'
2 | import { TranslationsContext } from 'hooks/TranslationsContext'
3 | import { getTranslation } from 'utils/translateTextHelpers'
4 |
5 | const useI18n = () => {
6 | const { translations } = useContext(TranslationsContext)
7 |
8 | /**
9 | * As a temporary fix memoize the translation function so it can be used in an effect.
10 | * It appears the TranslationsContext is always empty and is not currently used
11 | * TODO: Figure out if the context is used and if not, remove it.
12 | */
13 | return useCallback(
14 | (translationId: number, fallback: string) => {
15 | if (translations[0] === 'error') {
16 | return fallback
17 | }
18 | if (translations.length > 0) {
19 | return getTranslation(translations, translationId, fallback)
20 | }
21 | return fallback
22 | },
23 | [translations]
24 | )
25 | }
26 |
27 | export default useI18n
28 |
--------------------------------------------------------------------------------
/src/hooks/useInterval.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react'
2 |
3 | export default function useInterval(callback: () => void, delay: null | number, leading = true) {
4 | const savedCallback = useRef<() => void>()
5 |
6 | // Remember the latest callback.
7 | useEffect(() => {
8 | savedCallback.current = callback
9 | }, [callback])
10 |
11 | // Set up the interval.
12 | useEffect(() => {
13 | function tick() {
14 | const { current } = savedCallback
15 | if (current) {
16 | current()
17 | }
18 | }
19 |
20 | if (delay !== null) {
21 | if (leading) tick()
22 | const id = setInterval(tick, delay)
23 | return () => clearInterval(id)
24 | }
25 | return undefined
26 | }, [delay, leading])
27 | }
28 |
--------------------------------------------------------------------------------
/src/hooks/useIsWindowVisible.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useState } from 'react'
2 |
3 | const VISIBILITY_STATE_SUPPORTED = 'visibilityState' in document
4 |
5 | function isWindowVisible() {
6 | return !VISIBILITY_STATE_SUPPORTED || document.visibilityState !== 'hidden'
7 | }
8 |
9 | /**
10 | * Returns whether the window is currently visible to the user.
11 | */
12 | export default function useIsWindowVisible(): boolean {
13 | const [focused, setFocused] = useState(isWindowVisible())
14 | const listener = useCallback(() => {
15 | setFocused(isWindowVisible())
16 | }, [setFocused])
17 |
18 | useEffect(() => {
19 | if (!VISIBILITY_STATE_SUPPORTED) return undefined
20 |
21 | document.addEventListener('visibilitychange', listener)
22 | return () => {
23 | document.removeEventListener('visibilitychange', listener)
24 | }
25 | }, [listener])
26 |
27 | return focused
28 | }
29 |
--------------------------------------------------------------------------------
/src/hooks/useLast.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | /**
4 | * Returns the last value of type T that passes a filter function
5 | * @param value changing value
6 | * @param filterFn function that determines whether a given value should be considered for the last value
7 | */
8 | export default function useLast(
9 | value: T | undefined | null,
10 | filterFn?: (value: T | null | undefined) => boolean
11 | ): T | null | undefined {
12 | const [last, setLast] = useState(filterFn && filterFn(value) ? value : undefined)
13 | useEffect(() => {
14 | setLast((prev) => {
15 | const shouldUse: boolean = filterFn ? filterFn(value) : true
16 | if (shouldUse) return value
17 | return prev
18 | })
19 | }, [filterFn, value])
20 | return last
21 | }
22 |
23 | function isDefined(x: T | null | undefined): x is T {
24 | return x !== null && x !== undefined
25 | }
26 |
27 | /**
28 | * Returns the last truthy value of type T
29 | * @param value changing value
30 | */
31 | export function useLastTruthy(value: T | undefined | null): T | null | undefined {
32 | return useLast(value, isDefined)
33 | }
34 |
--------------------------------------------------------------------------------
/src/hooks/useOnClickOutside.tsx:
--------------------------------------------------------------------------------
1 | import { RefObject, useEffect, useRef } from 'react'
2 |
3 | export function useOnClickOutside(
4 | node: RefObject,
5 | handler: undefined | (() => void)
6 | ) {
7 | const handlerRef = useRef void)>(handler)
8 | useEffect(() => {
9 | handlerRef.current = handler
10 | }, [handler])
11 |
12 | useEffect(() => {
13 | const handleClickOutside = (e: MouseEvent) => {
14 | if (node.current?.contains(e.target as Node) ?? false) {
15 | return
16 | }
17 | if (handlerRef.current) handlerRef.current()
18 | }
19 |
20 | document.addEventListener('mousedown', handleClickOutside)
21 |
22 | return () => {
23 | document.removeEventListener('mousedown', handleClickOutside)
24 | }
25 | }, [node])
26 | }
27 |
28 | export default useOnClickOutside
29 |
--------------------------------------------------------------------------------
/src/hooks/useParsedQueryString.ts:
--------------------------------------------------------------------------------
1 | import { parse, ParsedQs } from 'qs'
2 | import { useMemo } from 'react'
3 | import { useLocation } from 'react-router-dom'
4 |
5 | export default function useParsedQueryString(): ParsedQs {
6 | const { search } = useLocation()
7 | return useMemo(
8 | () => (search && search.length > 1 ? parse(search, { parseArrays: false, ignoreQueryPrefix: true }) : {}),
9 | [search]
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/src/hooks/useTheme.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { ThemeContext as StyledThemeCopntext } from 'styled-components'
3 | import { ThemeContext } from '../ThemeContext'
4 |
5 | const useTheme = () => {
6 | const { isDark, toggleTheme } = useContext(ThemeContext)
7 | const theme = useContext(StyledThemeCopntext)
8 | return { isDark, toggleTheme, theme }
9 | }
10 |
11 | export default useTheme
12 |
--------------------------------------------------------------------------------
/src/hooks/useToast.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { kebabCase } from 'lodash'
3 | import { Toast, toastTypes } from '@pancakeswap-libs/uikit'
4 | import { useDispatch } from 'react-redux'
5 |
6 | import {
7 | push as pushToast,
8 | remove as removeToast,
9 | clear as clearToast
10 | } from 'state/actions'
11 |
12 | // Toasts
13 | const useToast = () => {
14 | const dispatch = useDispatch()
15 | const helpers = useMemo(() => {
16 | const push = (toast: Toast) => dispatch(pushToast(toast))
17 |
18 | return {
19 | toastError: (title: string, description?: string) => {
20 | return push({ id: kebabCase(title), type: toastTypes.DANGER, title, description })
21 | },
22 | toastInfo: (title: string, description?: string) => {
23 | return push({ id: kebabCase(title), type: toastTypes.INFO, title, description })
24 | },
25 | toastSuccess: (title: string, description?: string) => {
26 | return push({ id: kebabCase(title), type: toastTypes.SUCCESS, title, description })
27 | },
28 | toastWarning: (title: string, description?: string) => {
29 | return push({ id: kebabCase(title), type: toastTypes.WARNING, title, description })
30 | },
31 | push,
32 | remove: (id: string) => dispatch(removeToast(id)),
33 | clear: () => dispatch(clearToast()),
34 | }
35 | }, [dispatch])
36 |
37 | return helpers
38 | }
39 |
40 | export default useToast
--------------------------------------------------------------------------------
/src/hooks/useToggle.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useState } from 'react'
2 |
3 | export default function useToggle(initialState = false): [boolean, () => void] {
4 | const [state, setState] = useState(initialState)
5 | const toggle = useCallback(() => setState((prev) => !prev), [])
6 | return [state, toggle]
7 | }
8 |
--------------------------------------------------------------------------------
/src/i18n.ts:
--------------------------------------------------------------------------------
1 | import i18next from 'i18next'
2 | import { initReactI18next } from 'react-i18next'
3 | import XHR from 'i18next-xhr-backend'
4 | import LanguageDetector from 'i18next-browser-languagedetector'
5 |
6 | i18next
7 | .use(XHR)
8 | .use(LanguageDetector)
9 | .use(initReactI18next)
10 | .init({
11 | backend: {
12 | loadPath: `./locales/{{lng}}.json`
13 | },
14 | react: {
15 | useSuspense: true
16 | },
17 | fallbackLng: 'en',
18 | preload: ['en'],
19 | keySeparator: false,
20 | interpolation: { escapeValue: false }
21 | })
22 |
23 | export default i18next
24 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { StrictMode } from 'react'
2 | import ReactDOM from 'react-dom'
3 | import { ResetCSS } from '@pancakeswap-libs/uikit'
4 | import GlobalStyle from './style/Global'
5 | import App from './pages/App'
6 | import ApplicationUpdater from './state/application/updater'
7 | import ListsUpdater from './state/lists/updater'
8 | import MulticallUpdater from './state/multicall/updater'
9 | import TransactionUpdater from './state/transactions/updater'
10 | import ToastListener from './components/ToastListener'
11 | import Providers from './Providers'
12 | import 'inter-ui'
13 | import './i18n'
14 |
15 | if ('ethereum' in window) {
16 | (window.ethereum as any).autoRefreshOnNetworkChange = false
17 | }
18 |
19 | window.addEventListener('error', () => {
20 | localStorage?.removeItem('redux_localstorage_simple_lists')
21 | })
22 |
23 | ReactDOM.render(
24 |
25 |
26 | <>
27 |
28 |
29 |
30 |
31 |
32 | >
33 |
34 |
35 |
36 |
37 | ,
38 | document.getElementById('root')
39 | )
40 |
--------------------------------------------------------------------------------
/src/pages/AddLiquidity/PoolPriceBar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Currency, Percent, Price } from '@pancakeswap-libs/sdk'
3 | import { Text } from '@pancakeswap-libs/uikit'
4 | import { AutoColumn } from '../../components/Column'
5 | import { AutoRow } from '../../components/Row'
6 | import { ONE_BIPS } from '../../constants'
7 | import { Field } from '../../state/mint/actions'
8 |
9 | export function PoolPriceBar({
10 | currencies,
11 | noLiquidity,
12 | poolTokenPercentage,
13 | price,
14 | }: {
15 | currencies: { [field in Field]?: Currency }
16 | noLiquidity?: boolean
17 | poolTokenPercentage?: Percent
18 | price?: Price
19 | }) {
20 | return (
21 |
22 |
23 |
24 | {price?.toSignificant(6) ?? '-'}
25 |
26 | {currencies[Field.CURRENCY_B]?.symbol} per {currencies[Field.CURRENCY_A]?.symbol}
27 |
28 |
29 |
30 | {price?.invert()?.toSignificant(6) ?? '-'}
31 |
32 | {currencies[Field.CURRENCY_A]?.symbol} per {currencies[Field.CURRENCY_B]?.symbol}
33 |
34 |
35 |
36 |
37 | {noLiquidity && price
38 | ? '100'
39 | : (poolTokenPercentage?.lessThan(ONE_BIPS) ? '<0.01' : poolTokenPercentage?.toFixed(2)) ?? '0'}
40 | %
41 |
42 |
43 | Share of Pool
44 |
45 |
46 |
47 |
48 | )
49 | }
50 |
51 | export default PoolPriceBar
52 |
--------------------------------------------------------------------------------
/src/pages/AddLiquidity/redirects.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Redirect, RouteComponentProps } from 'react-router-dom'
3 | import AddLiquidity from './index'
4 |
5 | const OLD_PATH_STRUCTURE = /^(0x[a-fA-F0-9]{40})-(0x[a-fA-F0-9]{40})$/
6 | export function RedirectOldAddLiquidityPathStructure(props: RouteComponentProps<{ currencyIdA: string }>) {
7 | const {
8 | match: {
9 | params: { currencyIdA },
10 | },
11 | } = props
12 | const match = currencyIdA.match(OLD_PATH_STRUCTURE)
13 | if (match?.length) {
14 | return
15 | }
16 |
17 | return
18 | }
19 |
20 | export function RedirectDuplicateTokenIds(props: RouteComponentProps<{ currencyIdA: string; currencyIdB: string }>) {
21 | const {
22 | match: {
23 | params: { currencyIdA, currencyIdB },
24 | },
25 | } = props
26 | if (currencyIdA.toLowerCase() === currencyIdB.toLowerCase()) {
27 | return
28 | }
29 | return
30 | }
31 |
--------------------------------------------------------------------------------
/src/pages/AppBody.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { Card } from '@pancakeswap-libs/uikit'
4 |
5 | export const BodyWrapper = styled(Card)`
6 | position: relative;
7 | max-width: 436px;
8 | width: 100%;
9 | z-index: 5;
10 | `
11 |
12 | /**
13 | * The styled container element that wraps the content of most pages and the tabs.
14 | */
15 | export default function AppBody({ children }: { children: React.ReactNode }) {
16 | return {children}
17 | }
18 |
--------------------------------------------------------------------------------
/src/pages/Pool/styleds.tsx:
--------------------------------------------------------------------------------
1 | import { Text } from '@pancakeswap-libs/uikit'
2 | import styled from 'styled-components'
3 |
4 | export const Wrapper = styled.div`
5 | position: relative;
6 | `
7 |
8 | export const ClickableText = styled(Text)`
9 | :hover {
10 | cursor: pointer;
11 | }
12 | color: ${({ theme }) => theme.colors.primary};
13 | `
14 |
15 | export const Dots = styled.span`
16 | &::after {
17 | display: inline-block;
18 | animation: ellipsis 1.25s infinite;
19 | content: '.';
20 | width: 1em;
21 | text-align: left;
22 | }
23 | @keyframes ellipsis {
24 | 0% {
25 | content: '.';
26 | }
27 | 33% {
28 | content: '..';
29 | }
30 | 66% {
31 | content: '...';
32 | }
33 | }
34 | `
35 |
--------------------------------------------------------------------------------
/src/pages/RemoveLiquidity/redirects.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { RouteComponentProps, Redirect } from 'react-router-dom'
3 |
4 | const OLD_PATH_STRUCTURE = /^(0x[a-fA-F0-9]{40})-(0x[a-fA-F0-9]{40})$/
5 |
6 | export function RedirectOldRemoveLiquidityPathStructure({
7 | match: {
8 | params: { tokens },
9 | },
10 | }: RouteComponentProps<{ tokens: string }>) {
11 | if (!OLD_PATH_STRUCTURE.test(tokens)) {
12 | return
13 | }
14 | const [currency0, currency1] = tokens.split('-')
15 |
16 | return
17 | }
18 |
19 | export default RedirectOldRemoveLiquidityPathStructure
20 |
--------------------------------------------------------------------------------
/src/pages/Swap/redirects.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Redirect, RouteComponentProps } from 'react-router-dom'
3 |
4 | // Redirects to swap but only replace the pathname
5 | export function RedirectPathToSwapOnly({ location }: RouteComponentProps) {
6 | return
7 | }
8 |
9 | export default RedirectPathToSwapOnly
10 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module 'jazzicon' {
4 | export default function(diameter: number, seed: number): HTMLElement
5 | }
6 |
7 | declare module 'fortmatic'
8 |
9 | interface Window {
10 | ethereum?: {
11 | isMetaMask?: true
12 | on?: (...args: any[]) => void
13 | removeListener?: (...args: any[]) => void
14 | }
15 | web3?: any
16 | BinanceChain?: BinanceChain
17 | }
18 |
19 | declare module 'content-hash' {
20 | declare function decode(x: string): string
21 | declare function getCodec(x: string): string
22 | }
23 |
24 | declare module 'multihashes' {
25 | declare function decode(buff: Uint8Array): { code: number; name: string; length: number; digest: Uint8Array }
26 | declare function toB58String(hash: Uint8Array): string
27 | }
28 |
29 | interface BinanceChain {
30 | send: unknown
31 | enable: () => Promise
32 | on?: (method: string, listener: (...args: any[]) => void) => void
33 | removeListener?: (method: string, listener: (...args: any[]) => void) => void
34 | }
35 |
--------------------------------------------------------------------------------
/src/state/actions.ts:
--------------------------------------------------------------------------------
1 | export { clear, remove, push } from './toasts'
--------------------------------------------------------------------------------
/src/state/application/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 | import { TokenList } from '@uniswap/token-lists'
3 |
4 | export type PopupContent =
5 | | {
6 | txn: {
7 | hash: string
8 | success: boolean
9 | summary?: string
10 | }
11 | }
12 | | {
13 | listUpdate: {
14 | listUrl: string
15 | oldList: TokenList
16 | newList: TokenList
17 | auto: boolean
18 | }
19 | }
20 |
21 | export const updateBlockNumber = createAction<{ chainId: number; blockNumber: number }>('app/updateBlockNumber')
22 | export const toggleWalletModal = createAction('app/toggleWalletModal')
23 | export const toggleSettingsMenu = createAction('app/toggleSettingsMenu')
24 | export const addPopup = createAction<{ key?: string; removeAfterMs?: number | null; content: PopupContent }>(
25 | 'app/addPopup'
26 | )
27 | export const removePopup = createAction<{ key: string }>('app/removePopup')
28 |
--------------------------------------------------------------------------------
/src/state/application/hooks.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useMemo } from 'react'
2 | import { useSelector, useDispatch } from 'react-redux'
3 | import { useActiveWeb3React } from '../../hooks'
4 | import { addPopup, PopupContent, removePopup, toggleWalletModal, toggleSettingsMenu } from './actions'
5 | import { AppState } from '../index'
6 |
7 | export function useBlockNumber(): number | undefined {
8 | const { chainId } = useActiveWeb3React()
9 |
10 | return useSelector((state: AppState) => state.application.blockNumber[chainId ?? -1])
11 | }
12 |
13 | export function useWalletModalOpen(): boolean {
14 | return useSelector((state: AppState) => state.application.walletModalOpen)
15 | }
16 |
17 | export function useWalletModalToggle(): () => void {
18 | const dispatch = useDispatch()
19 | return useCallback(() => dispatch(toggleWalletModal()), [dispatch])
20 | }
21 |
22 | export function useSettingsMenuOpen(): boolean {
23 | return useSelector((state: AppState) => state.application.settingsMenuOpen)
24 | }
25 |
26 | export function useToggleSettingsMenu(): () => void {
27 | const dispatch = useDispatch()
28 | return useCallback(() => dispatch(toggleSettingsMenu()), [dispatch])
29 | }
30 |
31 | // returns a function that allows adding a popup
32 | export function useAddPopup(): (content: PopupContent, key?: string) => void {
33 | const dispatch = useDispatch()
34 |
35 | return useCallback(
36 | (content: PopupContent, key?: string) => {
37 | dispatch(addPopup({ content, key }))
38 | },
39 | [dispatch]
40 | )
41 | }
42 |
43 | // returns a function that allows removing a popup via its key
44 | export function useRemovePopup(): (key: string) => void {
45 | const dispatch = useDispatch()
46 | return useCallback(
47 | (key: string) => {
48 | dispatch(removePopup({ key }))
49 | },
50 | [dispatch]
51 | )
52 | }
53 |
54 | // get the list of active popups
55 | export function useActivePopups(): AppState['application']['popupList'] {
56 | const list = useSelector((state: AppState) => state.application.popupList)
57 | return useMemo(() => list.filter(item => item.show), [list])
58 | }
59 |
--------------------------------------------------------------------------------
/src/state/application/reducer.ts:
--------------------------------------------------------------------------------
1 | import { createReducer, nanoid } from '@reduxjs/toolkit'
2 | import {
3 | addPopup,
4 | PopupContent,
5 | removePopup,
6 | toggleWalletModal,
7 | toggleSettingsMenu,
8 | updateBlockNumber
9 | } from './actions'
10 |
11 | type PopupList = Array<{ key: string; show: boolean; content: PopupContent; removeAfterMs: number | null }>
12 |
13 | export interface ApplicationState {
14 | blockNumber: { [chainId: number]: number }
15 | popupList: PopupList
16 | walletModalOpen: boolean
17 | settingsMenuOpen: boolean
18 | }
19 |
20 | const initialState: ApplicationState = {
21 | blockNumber: {},
22 | popupList: [],
23 | walletModalOpen: false,
24 | settingsMenuOpen: false
25 | }
26 |
27 | export default createReducer(initialState, builder =>
28 | builder
29 | .addCase(updateBlockNumber, (state, action) => {
30 | const { chainId, blockNumber } = action.payload
31 | if (typeof state.blockNumber[chainId] !== 'number') {
32 | state.blockNumber[chainId] = blockNumber
33 | } else {
34 | state.blockNumber[chainId] = Math.max(blockNumber, state.blockNumber[chainId])
35 | }
36 | })
37 | .addCase(toggleWalletModal, state => {
38 | state.walletModalOpen = !state.walletModalOpen
39 | })
40 | .addCase(toggleSettingsMenu, state => {
41 | state.settingsMenuOpen = !state.settingsMenuOpen
42 | })
43 | .addCase(addPopup, (state, { payload: { content, key, removeAfterMs = 15000 } }) => {
44 | state.popupList = (key ? state.popupList.filter(popup => popup.key !== key) : state.popupList).concat([
45 | {
46 | key: key || nanoid(),
47 | show: true,
48 | content,
49 | removeAfterMs
50 | }
51 | ])
52 | })
53 | .addCase(removePopup, (state, { payload: { key } }) => {
54 | state.popupList.forEach(p => {
55 | if (p.key === key) {
56 | p.show = false
57 | }
58 | })
59 | })
60 | )
61 |
--------------------------------------------------------------------------------
/src/state/application/updater.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useState } from 'react'
2 | import { useDispatch } from 'react-redux'
3 | import { useActiveWeb3React } from '../../hooks'
4 | import useDebounce from '../../hooks/useDebounce'
5 | import useIsWindowVisible from '../../hooks/useIsWindowVisible'
6 | import { updateBlockNumber } from './actions'
7 |
8 | export default function Updater(): null {
9 | const { library, chainId } = useActiveWeb3React()
10 | const dispatch = useDispatch()
11 |
12 | const windowVisible = useIsWindowVisible()
13 |
14 | const [state, setState] = useState<{ chainId: number | undefined; blockNumber: number | null }>({
15 | chainId,
16 | blockNumber: null,
17 | })
18 |
19 | const blockNumberCallback = useCallback(
20 | (blockNumber: number) => {
21 | setState((s) => {
22 | if (chainId === s.chainId) {
23 | if (typeof s.blockNumber !== 'number') return { chainId, blockNumber }
24 | return { chainId, blockNumber: Math.max(blockNumber, s.blockNumber) }
25 | }
26 | return s
27 | })
28 | },
29 | [chainId, setState]
30 | )
31 |
32 | // attach/detach listeners
33 | useEffect(() => {
34 | if (!library || !chainId || !windowVisible) return undefined
35 |
36 | setState({ chainId, blockNumber: null })
37 |
38 | library
39 | .getBlockNumber()
40 | .then(blockNumberCallback)
41 | .catch((error) => console.error(`Failed to get block number for chainId: ${chainId}`, error))
42 |
43 | library.on('block', blockNumberCallback)
44 | return () => {
45 | library.removeListener('block', blockNumberCallback)
46 | }
47 | }, [dispatch, chainId, library, blockNumberCallback, windowVisible])
48 |
49 | const debouncedState = useDebounce(state, 100)
50 |
51 | useEffect(() => {
52 | if (!debouncedState.chainId || !debouncedState.blockNumber || !windowVisible) return
53 | dispatch(updateBlockNumber({ chainId: debouncedState.chainId, blockNumber: debouncedState.blockNumber }))
54 | }, [windowVisible, dispatch, debouncedState.blockNumber, debouncedState.chainId])
55 |
56 | return null
57 | }
58 |
--------------------------------------------------------------------------------
/src/state/burn/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 |
3 | export enum Field {
4 | LIQUIDITY_PERCENT = 'LIQUIDITY_PERCENT',
5 | LIQUIDITY = 'LIQUIDITY',
6 | CURRENCY_A = 'CURRENCY_A',
7 | CURRENCY_B = 'CURRENCY_B'
8 | }
9 |
10 | export const typeInput = createAction<{ field: Field; typedValue: string }>('burn/typeInputBurn')
11 |
--------------------------------------------------------------------------------
/src/state/burn/reducer.ts:
--------------------------------------------------------------------------------
1 | import { createReducer } from '@reduxjs/toolkit'
2 | import { Field, typeInput } from './actions'
3 |
4 | export interface BurnState {
5 | readonly independentField: Field
6 | readonly typedValue: string
7 | }
8 |
9 | const initialState: BurnState = {
10 | independentField: Field.LIQUIDITY_PERCENT,
11 | typedValue: '0'
12 | }
13 |
14 | export default createReducer(initialState, builder =>
15 | builder.addCase(typeInput, (state, { payload: { field, typedValue } }) => {
16 | return {
17 | ...state,
18 | independentField: field,
19 | typedValue
20 | }
21 | })
22 | )
23 |
--------------------------------------------------------------------------------
/src/state/global/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 |
3 | // fired once when the app reloads but before the app renders
4 | // allows any updates to be applied to store data loaded from localStorage
5 | export const updateVersion = createAction('global/updateVersion')
6 |
7 | export default createAction
8 |
--------------------------------------------------------------------------------
/src/state/index.ts:
--------------------------------------------------------------------------------
1 | import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
2 | import { save, load } from 'redux-localstorage-simple'
3 |
4 | import application from './application/reducer'
5 | import { updateVersion } from './global/actions'
6 | import user from './user/reducer'
7 | import transactions from './transactions/reducer'
8 | import swap from './swap/reducer'
9 | import mint from './mint/reducer'
10 | import lists from './lists/reducer'
11 | import burn from './burn/reducer'
12 | import multicall from './multicall/reducer'
13 | import toasts from './toasts'
14 | import { getThemeCache } from '../utils/theme'
15 |
16 | type MergedState = {
17 | user: {
18 | [key: string]: any
19 | }
20 | transactions: {
21 | [key: string]: any
22 | }
23 | }
24 | const PERSISTED_KEYS: string[] = ['user', 'transactions']
25 | const loadedState = load({ states: PERSISTED_KEYS }) as MergedState
26 | if (loadedState.user) {
27 | loadedState.user.userDarkMode = getThemeCache()
28 | }
29 |
30 | const store = configureStore({
31 | reducer: {
32 | application,
33 | user,
34 | transactions,
35 | swap,
36 | mint,
37 | burn,
38 | multicall,
39 | lists,
40 | toasts
41 | },
42 | middleware: [...getDefaultMiddleware({ thunk: false }), save({ states: PERSISTED_KEYS })],
43 | preloadedState: loadedState,
44 | })
45 |
46 | store.dispatch(updateVersion())
47 |
48 | export default store
49 |
50 | export type AppState = ReturnType
51 | export type AppDispatch = typeof store.dispatch
52 |
--------------------------------------------------------------------------------
/src/state/lists/actions.ts:
--------------------------------------------------------------------------------
1 | import { ActionCreatorWithPayload, createAction } from '@reduxjs/toolkit'
2 | import { TokenList, Version } from '@uniswap/token-lists'
3 |
4 | export const fetchTokenList: Readonly<{
5 | pending: ActionCreatorWithPayload<{ url: string; requestId: string }>
6 | fulfilled: ActionCreatorWithPayload<{ url: string; tokenList: TokenList; requestId: string }>
7 | rejected: ActionCreatorWithPayload<{ url: string; errorMessage: string; requestId: string }>
8 | }> = {
9 | pending: createAction('lists/fetchTokenList/pending'),
10 | fulfilled: createAction('lists/fetchTokenList/fulfilled'),
11 | rejected: createAction('lists/fetchTokenList/rejected')
12 | }
13 |
14 | export const acceptListUpdate = createAction('lists/acceptListUpdate')
15 | export const addList = createAction('lists/addList')
16 | export const removeList = createAction('lists/removeList')
17 | export const selectList = createAction('lists/selectList')
18 | export const rejectVersionUpdate = createAction('lists/rejectVersionUpdate')
19 |
--------------------------------------------------------------------------------
/src/state/mint/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 |
3 | export enum Field {
4 | CURRENCY_A = 'CURRENCY_A',
5 | CURRENCY_B = 'CURRENCY_B'
6 | }
7 |
8 | export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>('mint/typeInputMint')
9 | export const resetMintState = createAction('mint/resetMintState')
10 |
--------------------------------------------------------------------------------
/src/state/mint/reducer.test.ts:
--------------------------------------------------------------------------------
1 | import { createStore, Store } from 'redux'
2 |
3 | import { Field, typeInput } from './actions'
4 | import reducer, { MintState } from './reducer'
5 |
6 | describe('mint reducer', () => {
7 | let store: Store
8 |
9 | beforeEach(() => {
10 | store = createStore(reducer, {
11 | independentField: Field.CURRENCY_A,
12 | typedValue: '',
13 | otherTypedValue: ''
14 | })
15 | })
16 |
17 | describe('typeInput', () => {
18 | it('sets typed value', () => {
19 | store.dispatch(typeInput({ field: Field.CURRENCY_A, typedValue: '1.0', noLiquidity: false }))
20 | expect(store.getState()).toEqual({ independentField: Field.CURRENCY_A, typedValue: '1.0', otherTypedValue: '' })
21 | })
22 | it('clears other value', () => {
23 | store.dispatch(typeInput({ field: Field.CURRENCY_A, typedValue: '1.0', noLiquidity: false }))
24 | store.dispatch(typeInput({ field: Field.CURRENCY_B, typedValue: '1.0', noLiquidity: false }))
25 | expect(store.getState()).toEqual({ independentField: Field.CURRENCY_B, typedValue: '1.0', otherTypedValue: '' })
26 | })
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/src/state/mint/reducer.ts:
--------------------------------------------------------------------------------
1 | import { createReducer } from '@reduxjs/toolkit'
2 | import { Field, resetMintState, typeInput } from './actions'
3 |
4 | export interface MintState {
5 | readonly independentField: Field
6 | readonly typedValue: string
7 | readonly otherTypedValue: string // for the case when there's no liquidity
8 | }
9 |
10 | const initialState: MintState = {
11 | independentField: Field.CURRENCY_A,
12 | typedValue: '',
13 | otherTypedValue: ''
14 | }
15 |
16 | export default createReducer(initialState, builder =>
17 | builder
18 | .addCase(resetMintState, () => initialState)
19 | .addCase(typeInput, (state, { payload: { field, typedValue, noLiquidity } }) => {
20 | if (noLiquidity) {
21 | // they're typing into the field they've last typed in
22 | if (field === state.independentField) {
23 | return {
24 | ...state,
25 | independentField: field,
26 | typedValue
27 | }
28 | }
29 | // they're typing into a new field, store the other value
30 |
31 | return {
32 | ...state,
33 | independentField: field,
34 | typedValue,
35 | otherTypedValue: state.typedValue
36 | }
37 |
38 | }
39 | return {
40 | ...state,
41 | independentField: field,
42 | typedValue,
43 | otherTypedValue: ''
44 | }
45 |
46 | })
47 | )
48 |
--------------------------------------------------------------------------------
/src/state/multicall/actions.test.ts:
--------------------------------------------------------------------------------
1 | import { parseCallKey, toCallKey } from './actions'
2 |
3 | describe('actions', () => {
4 | describe('#parseCallKey', () => {
5 | it('does not throw for invalid address', () => {
6 | expect(parseCallKey('0x-0x')).toEqual({ address: '0x', callData: '0x' })
7 | })
8 | it('does not throw for invalid calldata', () => {
9 | expect(parseCallKey('0x6b175474e89094c44da98b954eedeac495271d0f-abc')).toEqual({
10 | address: '0x6b175474e89094c44da98b954eedeac495271d0f',
11 | callData: 'abc'
12 | })
13 | })
14 | it('throws for invalid format', () => {
15 | expect(() => parseCallKey('abc')).toThrow('Invalid call key: abc')
16 | })
17 | it('throws for uppercase calldata', () => {
18 | expect(parseCallKey('0x6b175474e89094c44da98b954eedeac495271d0f-0xabcD')).toEqual({
19 | address: '0x6b175474e89094c44da98b954eedeac495271d0f',
20 | callData: '0xabcD'
21 | })
22 | })
23 | it('parses pieces into address', () => {
24 | expect(parseCallKey('0x6b175474e89094c44da98b954eedeac495271d0f-0xabcd')).toEqual({
25 | address: '0x6b175474e89094c44da98b954eedeac495271d0f',
26 | callData: '0xabcd'
27 | })
28 | })
29 | })
30 |
31 | describe('#toCallKey', () => {
32 | it('throws for invalid address', () => {
33 | expect(() => toCallKey({ callData: '0x', address: '0x' })).toThrow('Invalid address: 0x')
34 | })
35 | it('throws for invalid calldata', () => {
36 | expect(() =>
37 | toCallKey({
38 | address: '0x6b175474e89094c44da98b954eedeac495271d0f',
39 | callData: 'abc'
40 | })
41 | ).toThrow('Invalid hex: abc')
42 | })
43 | it('throws for uppercase hex', () => {
44 | expect(() =>
45 | toCallKey({
46 | address: '0x6b175474e89094c44da98b954eedeac495271d0f',
47 | callData: '0xabcD'
48 | })
49 | ).toThrow('Invalid hex: 0xabcD')
50 | })
51 | it('concatenates address to data', () => {
52 | expect(toCallKey({ address: '0x6b175474e89094c44da98b954eedeac495271d0f', callData: '0xabcd' })).toEqual(
53 | '0x6b175474e89094c44da98b954eedeac495271d0f-0xabcd'
54 | )
55 | })
56 | })
57 | })
58 |
--------------------------------------------------------------------------------
/src/state/multicall/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 |
3 | export interface Call {
4 | address: string
5 | callData: string
6 | }
7 |
8 | const ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/
9 | const LOWER_HEX_REGEX = /^0x[a-f0-9]*$/
10 | export function toCallKey(call: Call): string {
11 | if (!ADDRESS_REGEX.test(call.address)) {
12 | throw new Error(`Invalid address: ${call.address}`)
13 | }
14 | if (!LOWER_HEX_REGEX.test(call.callData)) {
15 | throw new Error(`Invalid hex: ${call.callData}`)
16 | }
17 | return `${call.address}-${call.callData}`
18 | }
19 |
20 | export function parseCallKey(callKey: string): Call {
21 | const pcs = callKey.split('-')
22 | if (pcs.length !== 2) {
23 | throw new Error(`Invalid call key: ${callKey}`)
24 | }
25 | return {
26 | address: pcs[0],
27 | callData: pcs[1]
28 | }
29 | }
30 |
31 | export interface ListenerOptions {
32 | // how often this data should be fetched, by default 1
33 | readonly blocksPerFetch?: number
34 | }
35 |
36 | export const addMulticallListeners = createAction<{ chainId: number; calls: Call[]; options?: ListenerOptions }>(
37 | 'multicall/addMulticallListeners'
38 | )
39 | export const removeMulticallListeners = createAction<{ chainId: number; calls: Call[]; options?: ListenerOptions }>(
40 | 'multicall/removeMulticallListeners'
41 | )
42 | export const fetchingMulticallResults = createAction<{ chainId: number; calls: Call[]; fetchingBlockNumber: number }>(
43 | 'multicall/fetchingMulticallResults'
44 | )
45 | export const errorFetchingMulticallResults = createAction<{
46 | chainId: number
47 | calls: Call[]
48 | fetchingBlockNumber: number
49 | }>('multicall/errorFetchingMulticallResults')
50 | export const updateMulticallResults = createAction<{
51 | chainId: number
52 | blockNumber: number
53 | results: {
54 | [callKey: string]: string | null
55 | }
56 | }>('multicall/updateMulticallResults')
57 |
--------------------------------------------------------------------------------
/src/state/swap/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 |
3 | export enum Field {
4 | INPUT = 'INPUT',
5 | OUTPUT = 'OUTPUT'
6 | }
7 |
8 | export const selectCurrency = createAction<{ field: Field; currencyId: string }>('swap/selectCurrency')
9 | export const switchCurrencies = createAction('swap/switchCurrencies')
10 | export const typeInput = createAction<{ field: Field; typedValue: string }>('swap/typeInput')
11 | export const replaceSwapState = createAction<{
12 | field: Field
13 | typedValue: string
14 | inputCurrencyId?: string
15 | outputCurrencyId?: string
16 | recipient: string | null
17 | }>('swap/replaceSwapState')
18 | export const setRecipient = createAction<{ recipient: string | null }>('swap/setRecipient')
19 |
--------------------------------------------------------------------------------
/src/state/swap/reducer.test.ts:
--------------------------------------------------------------------------------
1 | import { createStore, Store } from 'redux'
2 | import { Field, selectCurrency } from './actions'
3 | import reducer, { SwapState } from './reducer'
4 |
5 | describe('swap reducer', () => {
6 | let store: Store
7 |
8 | beforeEach(() => {
9 | store = createStore(reducer, {
10 | [Field.OUTPUT]: { currencyId: '' },
11 | [Field.INPUT]: { currencyId: '' },
12 | typedValue: '',
13 | independentField: Field.INPUT,
14 | recipient: null
15 | })
16 | })
17 |
18 | describe('selectToken', () => {
19 | it('changes token', () => {
20 | store.dispatch(
21 | selectCurrency({
22 | field: Field.OUTPUT,
23 | currencyId: '0x0000'
24 | })
25 | )
26 |
27 | expect(store.getState()).toEqual({
28 | [Field.OUTPUT]: { currencyId: '0x0000' },
29 | [Field.INPUT]: { currencyId: '' },
30 | typedValue: '',
31 | independentField: Field.INPUT,
32 | recipient: null
33 | })
34 | })
35 | })
36 | })
37 |
--------------------------------------------------------------------------------
/src/state/toasts/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-param-reassign */
2 | import { Toast } from '@pancakeswap-libs/uikit'
3 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'
4 |
5 | const initialState: ToastsState = {
6 | data: [],
7 | }
8 |
9 | export interface ToastsState {
10 | data: Toast[]
11 | }
12 |
13 | export const toastsSlice = createSlice({
14 | name: 'toasts',
15 | initialState,
16 | reducers: {
17 | push: (state: ToastsState, action: PayloadAction) => {
18 | const { payload } = action
19 | const toastIndex = state.data.findIndex((toast) => toast.id === action.payload.id)
20 |
21 | // If id already matches remove it before adding it to the top of the stack
22 | if (toastIndex >= 0) {
23 | state.data.splice(toastIndex, 1)
24 | }
25 |
26 | state.data.unshift(payload)
27 | },
28 | remove: (state: ToastsState, action: PayloadAction) => {
29 | const toastIndex = state.data.findIndex((toast) => toast.id === action.payload)
30 |
31 | if (toastIndex >= 0) {
32 | state.data.splice(toastIndex, 1)
33 | }
34 | },
35 | clear: (state: ToastsState) => {
36 | state.data = []
37 | },
38 | },
39 | })
40 |
41 | // Actions
42 | export const { clear, remove, push } = toastsSlice.actions
43 |
44 | export default toastsSlice.reducer
45 |
--------------------------------------------------------------------------------
/src/state/transactions/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 | import { ChainId } from '@pancakeswap-libs/sdk'
3 |
4 | export interface SerializableTransactionReceipt {
5 | to: string
6 | from: string
7 | contractAddress: string
8 | transactionIndex: number
9 | blockHash: string
10 | transactionHash: string
11 | blockNumber: number
12 | status?: number
13 | }
14 |
15 | export const addTransaction = createAction<{
16 | chainId: ChainId
17 | hash: string
18 | from: string
19 | approval?: { tokenAddress: string; spender: string }
20 | summary?: string
21 | }>('transactions/addTransaction')
22 | export const clearAllTransactions = createAction<{ chainId: ChainId }>('transactions/clearAllTransactions')
23 | export const finalizeTransaction = createAction<{
24 | chainId: ChainId
25 | hash: string
26 | receipt: SerializableTransactionReceipt
27 | }>('transactions/finalizeTransaction')
28 | export const checkedTransaction = createAction<{
29 | chainId: ChainId
30 | hash: string
31 | blockNumber: number
32 | }>('transactions/checkedTransaction')
33 |
--------------------------------------------------------------------------------
/src/state/transactions/reducer.ts:
--------------------------------------------------------------------------------
1 | import { createReducer } from '@reduxjs/toolkit'
2 | import {
3 | addTransaction,
4 | checkedTransaction,
5 | clearAllTransactions,
6 | finalizeTransaction,
7 | SerializableTransactionReceipt
8 | } from './actions'
9 |
10 | const now = () => new Date().getTime()
11 |
12 | export interface TransactionDetails {
13 | hash: string
14 | approval?: { tokenAddress: string; spender: string }
15 | summary?: string
16 | receipt?: SerializableTransactionReceipt
17 | lastCheckedBlockNumber?: number
18 | addedTime: number
19 | confirmedTime?: number
20 | from: string
21 | }
22 |
23 | export interface TransactionState {
24 | [chainId: number]: {
25 | [txHash: string]: TransactionDetails
26 | }
27 | }
28 |
29 | export const initialState: TransactionState = {}
30 |
31 | export default createReducer(initialState, builder =>
32 | builder
33 | .addCase(addTransaction, (transactions, { payload: { chainId, from, hash, approval, summary } }) => {
34 | if (transactions[chainId]?.[hash]) {
35 | throw Error('Attempted to add existing transaction.')
36 | }
37 | const txs = transactions[chainId] ?? {}
38 | txs[hash] = { hash, approval, summary, from, addedTime: now() }
39 | transactions[chainId] = txs
40 | })
41 | .addCase(clearAllTransactions, (transactions, { payload: { chainId } }) => {
42 | if (!transactions[chainId]) return
43 | transactions[chainId] = {}
44 | })
45 | .addCase(checkedTransaction, (transactions, { payload: { chainId, hash, blockNumber } }) => {
46 | const tx = transactions[chainId]?.[hash]
47 | if (!tx) {
48 | return
49 | }
50 | if (!tx.lastCheckedBlockNumber) {
51 | tx.lastCheckedBlockNumber = blockNumber
52 | } else {
53 | tx.lastCheckedBlockNumber = Math.max(blockNumber, tx.lastCheckedBlockNumber)
54 | }
55 | })
56 | .addCase(finalizeTransaction, (transactions, { payload: { hash, chainId, receipt } }) => {
57 | const tx = transactions[chainId]?.[hash]
58 | if (!tx) {
59 | return
60 | }
61 | tx.receipt = receipt
62 | tx.confirmedTime = now()
63 | })
64 | )
65 |
--------------------------------------------------------------------------------
/src/state/transactions/updater.test.ts:
--------------------------------------------------------------------------------
1 | import { shouldCheck } from './updater'
2 |
3 | describe('transactions updater', () => {
4 | describe('shouldCheck', () => {
5 | it('returns true if no receipt and never checked', () => {
6 | expect(shouldCheck(10, { addedTime: 100 })).toEqual(true)
7 | })
8 | it('returns false if has receipt and never checked', () => {
9 | expect(shouldCheck(10, { addedTime: 100, receipt: {} })).toEqual(false)
10 | })
11 | it('returns true if has not been checked in 1 blocks', () => {
12 | expect(shouldCheck(10, { addedTime: new Date().getTime(), lastCheckedBlockNumber: 9 })).toEqual(true)
13 | })
14 | it('returns false if checked in last 3 blocks and greater than 20 minutes old', () => {
15 | expect(shouldCheck(10, { addedTime: new Date().getTime() - 21 * 60 * 1000, lastCheckedBlockNumber: 8 })).toEqual(
16 | false
17 | )
18 | })
19 | it('returns true if not checked in last 5 blocks and greater than 20 minutes old', () => {
20 | expect(shouldCheck(10, { addedTime: new Date().getTime() - 21 * 60 * 1000, lastCheckedBlockNumber: 5 })).toEqual(
21 | true
22 | )
23 | })
24 | it('returns false if checked in last 10 blocks and greater than 60 minutes old', () => {
25 | expect(shouldCheck(20, { addedTime: new Date().getTime() - 61 * 60 * 1000, lastCheckedBlockNumber: 11 })).toEqual(
26 | false
27 | )
28 | })
29 | it('returns true if checked in last 3 blocks and greater than 20 minutes old', () => {
30 | expect(shouldCheck(20, { addedTime: new Date().getTime() - 61 * 60 * 1000, lastCheckedBlockNumber: 10 })).toEqual(
31 | true
32 | )
33 | })
34 | })
35 | })
36 |
--------------------------------------------------------------------------------
/src/state/user/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@reduxjs/toolkit'
2 |
3 | export interface SerializedToken {
4 | chainId: number
5 | address: string
6 | decimals: number
7 | symbol?: string
8 | name?: string
9 | }
10 |
11 | export interface SerializedPair {
12 | token0: SerializedToken
13 | token1: SerializedToken
14 | }
15 |
16 | export const updateMatchesDarkMode = createAction<{ matchesDarkMode: boolean }>('user/updateMatchesDarkMode')
17 | export const updateUserDarkMode = createAction<{ userDarkMode: boolean }>('user/updateUserDarkMode')
18 | export const updateUserExpertMode = createAction<{ userExpertMode: boolean }>('user/updateUserExpertMode')
19 | export const updateUserSlippageTolerance = createAction<{ userSlippageTolerance: number }>(
20 | 'user/updateUserSlippageTolerance'
21 | )
22 | export const updateUserDeadline = createAction<{ userDeadline: number }>('user/updateUserDeadline')
23 | export const addSerializedToken = createAction<{ serializedToken: SerializedToken }>('user/addSerializedToken')
24 | export const removeSerializedToken = createAction<{ chainId: number; address: string }>('user/removeSerializedToken')
25 | export const addSerializedPair = createAction<{ serializedPair: SerializedPair }>('user/addSerializedPair')
26 | export const removeSerializedPair = createAction<{ chainId: number; tokenAAddress: string; tokenBAddress: string }>(
27 | 'user/removeSerializedPair'
28 | )
29 | export const muteAudio = createAction('user/muteAudio')
30 | export const unmuteAudio = createAction('user/unmuteAudio')
31 |
--------------------------------------------------------------------------------
/src/state/user/reducer.test.ts:
--------------------------------------------------------------------------------
1 | import { createStore, Store } from 'redux'
2 | import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE } from '../../constants'
3 | import { updateVersion } from '../global/actions'
4 | import reducer, { initialState, UserState } from './reducer'
5 |
6 | describe('swap reducer', () => {
7 | let store: Store
8 |
9 | beforeEach(() => {
10 | store = createStore(reducer, initialState)
11 | })
12 |
13 | describe('updateVersion', () => {
14 | it('has no timestamp originally', () => {
15 | expect(store.getState().lastUpdateVersionTimestamp).toBeUndefined()
16 | })
17 | it('sets the lastUpdateVersionTimestamp', () => {
18 | const time = new Date().getTime()
19 | store.dispatch(updateVersion())
20 | expect(store.getState().lastUpdateVersionTimestamp).toBeGreaterThanOrEqual(time)
21 | })
22 | it('sets allowed slippage and deadline', () => {
23 | store = createStore(reducer, {
24 | ...initialState,
25 | userDeadline: undefined,
26 | userSlippageTolerance: undefined
27 | } as any)
28 | store.dispatch(updateVersion())
29 | expect(store.getState().userDeadline).toEqual(DEFAULT_DEADLINE_FROM_NOW)
30 | expect(store.getState().userSlippageTolerance).toEqual(INITIAL_ALLOWED_SLIPPAGE)
31 | })
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/src/style/Global.ts:
--------------------------------------------------------------------------------
1 | import { createGlobalStyle } from 'styled-components'
2 |
3 | const GlobalStyle = createGlobalStyle`
4 | body {
5 | background-color: ${({ theme }) => theme.colors.background};
6 |
7 | img {
8 | height: auto;
9 | max-width: 100%;
10 | }
11 | }
12 | `
13 |
14 | export default GlobalStyle
15 |
--------------------------------------------------------------------------------
/src/style/styled.d.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-unresolved
2 | import { PancakeTheme } from '@pancakeswap-libs/uikit/dist/theme'
3 |
4 | declare module 'styled-components' {
5 | /* eslint-disable @typescript-eslint/no-empty-interface */
6 | export interface DefaultTheme extends PancakeTheme {}
7 | }
8 |
--------------------------------------------------------------------------------
/src/utils/chunkArray.test.ts:
--------------------------------------------------------------------------------
1 | import chunkArray from './chunkArray'
2 |
3 | describe('#chunkArray', () => {
4 | it('size 1', () => {
5 | expect(chunkArray([1, 2, 3], 1)).toEqual([[1], [2], [3]])
6 | })
7 | it('size 0 throws', () => {
8 | expect(() => chunkArray([1, 2, 3], 0)).toThrow('maxChunkSize must be gte 1')
9 | })
10 | it('size gte items', () => {
11 | expect(chunkArray([1, 2, 3], 3)).toEqual([[1, 2, 3]])
12 | expect(chunkArray([1, 2, 3], 4)).toEqual([[1, 2, 3]])
13 | })
14 | it('size exact half', () => {
15 | expect(chunkArray([1, 2, 3, 4], 2)).toEqual([
16 | [1, 2],
17 | [3, 4]
18 | ])
19 | })
20 | it('evenly distributes', () => {
21 | const chunked = chunkArray([...Array(100).keys()], 40)
22 |
23 | expect(chunked).toEqual([
24 | [...Array(34).keys()],
25 | [...Array(34).keys()].map(i => i + 34),
26 | [...Array(32).keys()].map(i => i + 68)
27 | ])
28 |
29 | expect(chunked[0][0]).toEqual(0)
30 | expect(chunked[2][31]).toEqual(99)
31 | })
32 | })
33 |
--------------------------------------------------------------------------------
/src/utils/chunkArray.ts:
--------------------------------------------------------------------------------
1 | // chunks array into chunks of maximum size
2 | // evenly distributes items among the chunks
3 | export default function chunkArray(items: T[], maxChunkSize: number): T[][] {
4 | if (maxChunkSize < 1) throw new Error('maxChunkSize must be gte 1')
5 | if (items.length <= maxChunkSize) return [items]
6 |
7 | const numChunks: number = Math.ceil(items.length / maxChunkSize)
8 | const chunkSize = Math.ceil(items.length / numChunks)
9 |
10 | return [...Array(numChunks).keys()].map(ix => items.slice(ix * chunkSize, ix * chunkSize + chunkSize))
11 | }
12 |
--------------------------------------------------------------------------------
/src/utils/contenthashToUri.test.skip.ts:
--------------------------------------------------------------------------------
1 | import contenthashToUri, { hexToUint8Array } from './contenthashToUri'
2 |
3 | // this test is skipped for now because importing CID results in
4 | // TypeError: TextDecoder is not a constructor
5 |
6 | describe('#contenthashToUri', () => {
7 | it('1inch.tokens.eth contenthash', () => {
8 | expect(contenthashToUri('0xe3010170122013e051d1cfff20606de36845d4fe28deb9861a319a5bc8596fa4e610e8803918')).toEqual(
9 | 'ipfs://QmPgEqyV3m8SB52BS2j2mJpu9zGprhj2BGCHtRiiw2fdM1'
10 | )
11 | })
12 | it('uniswap.eth contenthash', () => {
13 | expect(contenthashToUri('0xe5010170000f6170702e756e69737761702e6f7267')).toEqual('ipns://app.uniswap.org')
14 | })
15 | })
16 |
17 | describe('#hexToUint8Array', () => {
18 | it('common case', () => {
19 | expect(hexToUint8Array('0x010203fdfeff')).toEqual(new Uint8Array([1, 2, 3, 253, 254, 255]))
20 | })
21 | })
22 |
--------------------------------------------------------------------------------
/src/utils/contenthashToUri.ts:
--------------------------------------------------------------------------------
1 | import CID from 'cids'
2 | import { getCodec, rmPrefix } from 'multicodec'
3 | import { decode, toB58String } from 'multihashes'
4 |
5 | export function hexToUint8Array(hex: string): Uint8Array {
6 | hex = hex.startsWith('0x') ? hex.substr(2) : hex
7 | if (hex.length % 2 !== 0) throw new Error('hex must have length that is multiple of 2')
8 | const arr = new Uint8Array(hex.length / 2)
9 | for (let i = 0; i < arr.length; i++) {
10 | arr[i] = parseInt(hex.substr(i * 2, 2), 16)
11 | }
12 | return arr
13 | }
14 |
15 | const UTF_8_DECODER = new TextDecoder()
16 |
17 | /**
18 | * Returns the URI representation of the content hash for supported codecs
19 | * @param contenthash to decode
20 | */
21 | export default function contenthashToUri(contenthash: string): string {
22 | const buff = hexToUint8Array(contenthash)
23 | const codec = getCodec(buff as Buffer) // the typing is wrong for @types/multicodec
24 | switch (codec) {
25 | case 'ipfs-ns': {
26 | const data = rmPrefix(buff as Buffer)
27 | const cid = new CID(data)
28 | return `ipfs://${toB58String(cid.multihash)}`
29 | }
30 | case 'ipns-ns': {
31 | const data = rmPrefix(buff as Buffer)
32 | const cid = new CID(data)
33 | const multihash = decode(cid.multihash)
34 | if (multihash.name === 'identity') {
35 | return `ipns://${UTF_8_DECODER.decode(multihash.digest).trim()}`
36 | }
37 | return `ipns://${toB58String(cid.multihash)}`
38 |
39 | }
40 | default:
41 | throw new Error(`Unrecognized codec: ${codec}`)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/utils/currencyId.ts:
--------------------------------------------------------------------------------
1 | import { Currency, ETHER, Token } from '@pancakeswap-libs/sdk'
2 |
3 | export function currencyId(currency: Currency): string {
4 | if (currency === ETHER) return 'BNB'
5 | if (currency instanceof Token) return currency.address
6 | throw new Error('invalid currency')
7 | }
8 |
9 | export default currencyId
10 |
--------------------------------------------------------------------------------
/src/utils/getLibrary.ts:
--------------------------------------------------------------------------------
1 | import { Web3Provider } from '@ethersproject/providers'
2 |
3 | export default function getLibrary(provider: any): Web3Provider {
4 | const library = new Web3Provider(provider)
5 | library.pollingInterval = 15000
6 | return library
7 | }
8 |
--------------------------------------------------------------------------------
/src/utils/isZero.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Returns true if the string value is zero in hex
3 | * @param hexNumberString
4 | */
5 | export default function isZero(hexNumberString: string) {
6 | return /^0x0*$/.test(hexNumberString)
7 | }
8 |
--------------------------------------------------------------------------------
/src/utils/listVersionLabel.ts:
--------------------------------------------------------------------------------
1 | import { Version } from '@uniswap/token-lists'
2 |
3 | export default function listVersionLabel(version: Version): string {
4 | return `v${version.major}.${version.minor}.${version.patch}`
5 | }
6 |
--------------------------------------------------------------------------------
/src/utils/maxAmountSpend.ts:
--------------------------------------------------------------------------------
1 | import { CurrencyAmount, ETHER, JSBI } from '@pancakeswap-libs/sdk'
2 | import { MIN_ETH } from '../constants'
3 |
4 | /**
5 | * Given some token amount, return the max that can be spent of it
6 | * @param currencyAmount to return max of
7 | */
8 | export function maxAmountSpend(currencyAmount?: CurrencyAmount): CurrencyAmount | undefined {
9 | if (!currencyAmount) return undefined
10 | if (currencyAmount.currency === ETHER) {
11 | if (JSBI.greaterThan(currencyAmount.raw, MIN_ETH)) {
12 | return CurrencyAmount.ether(JSBI.subtract(currencyAmount.raw, MIN_ETH))
13 | }
14 | return CurrencyAmount.ether(JSBI.BigInt(0))
15 | }
16 | return currencyAmount
17 | }
18 |
19 | export default maxAmountSpend
20 |
--------------------------------------------------------------------------------
/src/utils/parseENSAddress.test.ts:
--------------------------------------------------------------------------------
1 | import { parseENSAddress } from './parseENSAddress'
2 |
3 | describe('parseENSAddress', () => {
4 | it('test cases', () => {
5 | expect(parseENSAddress('hello.eth')).toEqual({ ensName: 'hello.eth', ensPath: undefined })
6 | expect(parseENSAddress('hello.eth/')).toEqual({ ensName: 'hello.eth', ensPath: '/' })
7 | expect(parseENSAddress('hello.world.eth/')).toEqual({ ensName: 'hello.world.eth', ensPath: '/' })
8 | expect(parseENSAddress('hello.world.eth/abcdef')).toEqual({ ensName: 'hello.world.eth', ensPath: '/abcdef' })
9 | expect(parseENSAddress('abso.lutely')).toEqual(undefined)
10 | expect(parseENSAddress('abso.lutely.eth')).toEqual({ ensName: 'abso.lutely.eth', ensPath: undefined })
11 | expect(parseENSAddress('eth')).toEqual(undefined)
12 | expect(parseENSAddress('eth/hello-world')).toEqual(undefined)
13 | })
14 | })
15 |
--------------------------------------------------------------------------------
/src/utils/parseENSAddress.ts:
--------------------------------------------------------------------------------
1 | const ENS_NAME_REGEX = /^(([a-zA-Z0-9]+\.)+)eth(\/.*)?$/
2 |
3 | export function parseENSAddress(ensAddress: string): { ensName: string; ensPath: string | undefined } | undefined {
4 | const match = ensAddress.match(ENS_NAME_REGEX)
5 | if (!match) return undefined
6 | return { ensName: `${match[1].toLowerCase()}eth`, ensPath: match[3] }
7 | }
8 |
9 | export default parseENSAddress
10 |
--------------------------------------------------------------------------------
/src/utils/prices.test.ts:
--------------------------------------------------------------------------------
1 | import { ChainId, JSBI, Pair, Route, Token, TokenAmount, Trade, TradeType } from '@pancakeswap-libs/sdk'
2 | import { computeTradePriceBreakdown } from './prices'
3 |
4 | describe('prices', () => {
5 | const token1 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000001', 18)
6 | const token2 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000002', 18)
7 | const token3 = new Token(ChainId.MAINNET, '0x0000000000000000000000000000000000000003', 18)
8 |
9 | const pair12 = new Pair(new TokenAmount(token1, JSBI.BigInt(10000)), new TokenAmount(token2, JSBI.BigInt(20000)))
10 | const pair23 = new Pair(new TokenAmount(token2, JSBI.BigInt(20000)), new TokenAmount(token3, JSBI.BigInt(30000)))
11 |
12 | describe('computeTradePriceBreakdown', () => {
13 | it('returns undefined for undefined', () => {
14 | expect(computeTradePriceBreakdown(undefined)).toEqual({
15 | priceImpactWithoutFee: undefined,
16 | realizedLPFee: undefined,
17 | })
18 | })
19 |
20 | it('correct realized lp fee for single hop', () => {
21 | expect(
22 | computeTradePriceBreakdown(
23 | new Trade(new Route([pair12], token1), new TokenAmount(token1, JSBI.BigInt(1000)), TradeType.EXACT_INPUT)
24 | ).realizedLPFee
25 | ).toEqual(new TokenAmount(token1, JSBI.BigInt(2)))
26 | })
27 |
28 | it('correct realized lp fee for double hop', () => {
29 | expect(
30 | computeTradePriceBreakdown(
31 | new Trade(
32 | new Route([pair12, pair23], token1),
33 | new TokenAmount(token1, JSBI.BigInt(1000)),
34 | TradeType.EXACT_INPUT
35 | )
36 | ).realizedLPFee
37 | ).toEqual(new TokenAmount(token1, JSBI.BigInt(3)))
38 | })
39 | })
40 | })
41 |
--------------------------------------------------------------------------------
/src/utils/resolveENSContentHash.ts:
--------------------------------------------------------------------------------
1 | import { Contract } from '@ethersproject/contracts'
2 | import { Provider } from '@ethersproject/abstract-provider'
3 | import { namehash } from 'ethers/lib/utils'
4 |
5 | const REGISTRAR_ABI = [
6 | {
7 | constant: true,
8 | inputs: [
9 | {
10 | name: 'node',
11 | type: 'bytes32'
12 | }
13 | ],
14 | name: 'resolver',
15 | outputs: [
16 | {
17 | name: 'resolverAddress',
18 | type: 'address'
19 | }
20 | ],
21 | payable: false,
22 | stateMutability: 'view',
23 | type: 'function'
24 | }
25 | ]
26 | const REGISTRAR_ADDRESS = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'
27 |
28 | const RESOLVER_ABI = [
29 | {
30 | constant: true,
31 | inputs: [
32 | {
33 | internalType: 'bytes32',
34 | name: 'node',
35 | type: 'bytes32'
36 | }
37 | ],
38 | name: 'contenthash',
39 | outputs: [
40 | {
41 | internalType: 'bytes',
42 | name: '',
43 | type: 'bytes'
44 | }
45 | ],
46 | payable: false,
47 | stateMutability: 'view',
48 | type: 'function'
49 | }
50 | ]
51 |
52 | // cache the resolver contracts since most of them are the public resolver
53 | function resolverContract(resolverAddress: string, provider: Provider): Contract {
54 | return new Contract(resolverAddress, RESOLVER_ABI, provider)
55 | }
56 |
57 | /**
58 | * Fetches and decodes the result of an ENS contenthash lookup on mainnet to a URI
59 | * @param ensName to resolve
60 | * @param provider provider to use to fetch the data
61 | */
62 | export default async function resolveENSContentHash(ensName: string, provider: Provider): Promise {
63 | const ensRegistrarContract = new Contract(REGISTRAR_ADDRESS, REGISTRAR_ABI, provider)
64 | const hash = namehash(ensName)
65 | const resolverAddress = await ensRegistrarContract.resolver(hash)
66 | return resolverContract(resolverAddress, provider).contenthash(hash)
67 | }
68 |
--------------------------------------------------------------------------------
/src/utils/retry.ts:
--------------------------------------------------------------------------------
1 | function wait(ms: number): Promise {
2 | return new Promise((resolve) => setTimeout(resolve, ms))
3 | }
4 |
5 | function waitRandom(min: number, max: number): Promise {
6 | return wait(min + Math.round(Math.random() * Math.max(0, max - min)))
7 | }
8 |
9 | /**
10 | * This error is thrown if the function is cancelled before completing
11 | */
12 | export class CancelledError extends Error {
13 | constructor() {
14 | super('Cancelled')
15 | }
16 | }
17 |
18 | /**
19 | * Throw this error if the function should retry
20 | */
21 | export class RetryableError extends Error {}
22 |
23 | /**
24 | * Retries the function that returns the promise until the promise successfully resolves up to n retries
25 | * @param fn function to retry
26 | * @param n how many times to retry
27 | * @param minWait min wait between retries in ms
28 | * @param maxWait max wait between retries in ms
29 | */
30 | export function retry(
31 | fn: () => Promise,
32 | { n, minWait, maxWait }: { n: number; minWait: number; maxWait: number }
33 | ): { promise: Promise; cancel: () => void } {
34 | let completed = false
35 | let rejectCancelled: (error: Error) => void
36 | // eslint-disable-next-line no-async-promise-executor
37 | const promise = new Promise(async (resolve, reject) => {
38 | rejectCancelled = reject
39 | // eslint-disable-next-line no-constant-condition
40 | while (true) {
41 | let result: T
42 | try {
43 | result = await fn()
44 | if (!completed) {
45 | resolve(result)
46 | completed = true
47 | }
48 | break
49 | } catch (error) {
50 | if (completed) {
51 | break
52 | }
53 | if (n <= 0 || !(error instanceof RetryableError)) {
54 | reject(error)
55 | completed = true
56 | break
57 | }
58 | n--
59 | }
60 | await waitRandom(minWait, maxWait)
61 | }
62 | })
63 | return {
64 | promise,
65 | cancel: () => {
66 | if (completed) return
67 | completed = true
68 | rejectCancelled(new CancelledError())
69 | },
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/utils/theme.ts:
--------------------------------------------------------------------------------
1 | const CACHE_KEY = 'IS_DARK'
2 |
3 | export function getThemeCache(): boolean | null {
4 | let cache = null
5 | try {
6 | const rawCache = localStorage.getItem(CACHE_KEY)
7 | if (rawCache) {
8 | cache = JSON.parse(rawCache)
9 | }
10 | } catch (error) {
11 | console.error(error)
12 | }
13 |
14 | return cache
15 | }
16 |
17 | export function setThemeCache(isDark: boolean) {
18 | try {
19 | localStorage.setItem(CACHE_KEY, JSON.stringify(isDark))
20 | } catch (error) {
21 | console.error(error)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/utils/translateTextHelpers.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { TranslationsContext } from '../hooks/TranslationsContext'
3 |
4 | const variableRegex = /%(.*?)%/
5 |
6 | const replaceDynamicString = (foundTranslation: string, fallback: string) => {
7 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
8 | const stringToReplace = variableRegex.exec(foundTranslation)![0]
9 | const indexToReplace = foundTranslation.split(' ').indexOf(stringToReplace)
10 | const fallbackValueAtIndex = fallback.split(' ')[indexToReplace]
11 | return foundTranslation.replace(stringToReplace, fallbackValueAtIndex)
12 | }
13 |
14 | export const getTranslation = (translations: Array, translationId: number, fallback: string) => {
15 | const foundTranslation = translations.find((translation) => {
16 | return translation.data.stringId === translationId
17 | })
18 | if (foundTranslation) {
19 | const translatedString = foundTranslation.data.text
20 | const includesVariable = translatedString.includes('%')
21 | if (includesVariable) {
22 | return replaceDynamicString(translatedString, fallback)
23 | }
24 | return translatedString
25 | }
26 | return fallback
27 | }
28 |
29 | export const TranslateString = (translationId: number, fallback: string) => {
30 | const { translations } = useContext(TranslationsContext)
31 | if (translations[0] === 'error') {
32 | return fallback
33 | }
34 | if (translations.length > 0) {
35 | return getTranslation(translations, translationId, fallback)
36 | }
37 | return null
38 | }
39 |
--------------------------------------------------------------------------------
/src/utils/uriToHttp.test.ts:
--------------------------------------------------------------------------------
1 | import uriToHttp from './uriToHttp'
2 |
3 | describe('uriToHttp', () => {
4 | it('returns .eth.link for ens names', () => {
5 | expect(uriToHttp('t2crtokens.eth')).toEqual([])
6 | })
7 | it('returns https first for http', () => {
8 | expect(uriToHttp('http://test.com')).toEqual(['https://test.com', 'http://test.com'])
9 | })
10 | it('returns https for https', () => {
11 | expect(uriToHttp('https://test.com')).toEqual(['https://test.com'])
12 | })
13 | it('returns ipfs gateways for ipfs:// urls', () => {
14 | expect(uriToHttp('ipfs://QmV8AfDE8GFSGQvt3vck8EwAzsPuNTmtP8VcQJE3qxRPaZ')).toEqual([
15 | 'https://cloudflare-ipfs.com/ipfs/QmV8AfDE8GFSGQvt3vck8EwAzsPuNTmtP8VcQJE3qxRPaZ/',
16 | 'https://ipfs.io/ipfs/QmV8AfDE8GFSGQvt3vck8EwAzsPuNTmtP8VcQJE3qxRPaZ/'
17 | ])
18 | })
19 | it('returns ipns gateways for ipns:// urls', () => {
20 | expect(uriToHttp('ipns://app.uniswap.org')).toEqual([
21 | 'https://cloudflare-ipfs.com/ipns/app.uniswap.org/',
22 | 'https://ipfs.io/ipns/app.uniswap.org/'
23 | ])
24 | })
25 | it('returns empty array for invalid scheme', () => {
26 | expect(uriToHttp('blah:test')).toEqual([])
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/src/utils/uriToHttp.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Given a URI that may be ipfs, ipns, http, or https protocol, return the fetch-able http(s) URLs for the same content
3 | * @param uri to convert to fetch-able http url
4 | */
5 | export default function uriToHttp(uri: string): string[] {
6 | const protocol = uri.split(':')[0].toLowerCase()
7 | switch (protocol) {
8 | case 'https':
9 | return [uri]
10 | case 'http':
11 | return [`https${ uri.substr(4)}`, uri]
12 | case 'ipfs':
13 | const hash = uri.match(/^ipfs:(\/\/)?(.*)$/i)?.[2]
14 | return [`https://cloudflare-ipfs.com/ipfs/${hash}/`, `https://ipfs.io/ipfs/${hash}/`]
15 | case 'ipns':
16 | const name = uri.match(/^ipns:(\/\/)?(.*)$/i)?.[2]
17 | return [`https://cloudflare-ipfs.com/ipns/${name}/`, `https://ipfs.io/ipns/${name}/`]
18 | default:
19 | return []
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/utils/useDebouncedChangeHandler.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useRef, useState } from 'react'
2 |
3 | /**
4 | * Easy way to debounce the handling of a rapidly changing value, e.g. a changing slider input
5 | * @param value value that is rapidly changing
6 | * @param onChange change handler that should receive the debounced updates to the value
7 | * @param debouncedMs how long we should wait for changes to be applied
8 | */
9 | export default function useDebouncedChangeHandler(
10 | value: T,
11 | onChange: (newValue: T) => void,
12 | debouncedMs = 100
13 | ): [T, (value: T) => void] {
14 | const [inner, setInner] = useState(() => value)
15 | const timer = useRef>()
16 |
17 | const onChangeInner = useCallback(
18 | (newValue: T) => {
19 | setInner(newValue)
20 | if (timer.current) {
21 | clearTimeout(timer.current)
22 | }
23 | timer.current = setTimeout(() => {
24 | onChange(newValue)
25 | timer.current = undefined
26 | }, debouncedMs)
27 | },
28 | [debouncedMs, onChange]
29 | )
30 |
31 | useEffect(() => {
32 | if (timer.current) {
33 | clearTimeout(timer.current)
34 | timer.current = undefined
35 | }
36 | setInner(value)
37 | }, [value])
38 |
39 | return [inner, onChangeInner]
40 | }
41 |
--------------------------------------------------------------------------------
/src/utils/wrappedCurrency.ts:
--------------------------------------------------------------------------------
1 | import { ChainId, Currency, CurrencyAmount, ETHER, Token, TokenAmount, WETH } from '@pancakeswap-libs/sdk'
2 |
3 | export function wrappedCurrency(currency: Currency | undefined, chainId: ChainId | undefined): Token | undefined {
4 | // eslint-disable-next-line no-nested-ternary
5 | return chainId && currency === ETHER ? WETH[chainId] : currency instanceof Token ? currency : undefined
6 | }
7 |
8 | export function wrappedCurrencyAmount(
9 | currencyAmount: CurrencyAmount | undefined,
10 | chainId: ChainId | undefined
11 | ): TokenAmount | undefined {
12 | const token = currencyAmount && chainId ? wrappedCurrency(currencyAmount.currency, chainId) : undefined
13 | return token && currencyAmount ? new TokenAmount(token, currencyAmount.raw) : undefined
14 | }
15 |
16 | export function unwrappedToken(token: Token): Currency {
17 | if (token.equals(WETH[token.chainId])) return ETHER
18 | return token
19 | }
20 |
--------------------------------------------------------------------------------
/static.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": "build/"
3 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "src",
4 | "target": "es5",
5 | "lib": ["dom", "dom.iterable", "esnext"],
6 | "allowJs": true,
7 | "skipLibCheck": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "strict": true,
13 | "alwaysStrict": true,
14 | "strictNullChecks": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "noImplicitAny": false,
17 | "noImplicitThis": true,
18 | "noImplicitReturns": true,
19 | "moduleResolution": "node",
20 | "resolveJsonModule": true,
21 | "isolatedModules": true,
22 | "jsx": "react-jsx",
23 | "downlevelIteration": true,
24 | "allowSyntheticDefaultImports": true,
25 | "types": ["react-spring", "jest"]
26 | },
27 | "exclude": ["node_modules", "cypress"],
28 | "include": ["src"]
29 | }
30 |
--------------------------------------------------------------------------------