├── .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 | [![Netlify Status](https://api.netlify.com/api/v1/badges/c6ef7e73-4a84-410d-83b0-b89326787dff/deploy-status)](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 | {alt} { 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 | --------------------------------------------------------------------------------