Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Tokens Dropdown): tokens by network and native tokens #157

Merged
merged 10 commits into from
Jul 8, 2024
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ PUBLIC_INCLUDE_TESTNETS=true
# WalletConnect Project ID
PUBLIC_WALLETCONNECT_PROJECT_ID=

# Native token address
PUBLIC_NATIVE_TOKEN_ADDRESS=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE

# RPCs
PUBLIC_RPC_ARBITRUM=
PUBLIC_RPC_ARBITRUM_SEPOLIA=
Expand Down
116 changes: 23 additions & 93 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,106 +1,40 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>dAppBooster</title>
<!-- Standard meta tags -->
<meta
content='A modern blockchain boilerplate built to quickly get you started with your next project.'
content="A modern blockchain boilerplate built to quickly get you started with your next project."
name="description"
/>
<!-- Open graph meta tags -->
<meta content="dAppBooster" property="og:title" />
<meta content="" property="og:url" />
<meta content="/share/ogImage.jpg" property="og:image" />
<meta content="website" property="og:type" />
<meta
content='dAppBooster'
property="og:title"
/>
<meta
content=''
property="og:url"
/>
<meta
content='/share/ogImage.jpg'
property="og:image"
/>
<meta
content="website"
property="og:type"
/>
<meta
content='A modern blockchain boilerplate built to quickly get you started with your next project.'
content="A modern blockchain boilerplate built to quickly get you started with your next project."
property="og:description"
/>
<!-- Twitter meta tags -->
<meta
content="summary_large_image"
name="twitter:card"
/>
<meta
content='dAppBooster'
name="twitter:site"
/>
<meta
content='@'
name="twitter:creator"
/>
<meta content="summary_large_image" name="twitter:card" />
<meta content="dAppBooster" name="twitter:site" />
<meta content="@" name="twitter:creator" />
<!-- Favicons -->
<link
rel="icon"
type="image/svg+xml"
href="/favicon/favicon.svg"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/favicon/apple-touch-icon.png"
>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon/favicon-32x32.png"
>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon/favicon-16x16.png"
>
<link
rel="manifest"
href="/favicon/site.webmanifest"
>
<link
rel="mask-icon"
href="/favicon/safari-pinned-tab.svg"
color="#5bbad5"
>
<meta
name="msapplication-config"
content="/favicon/browserconfig.xml"
/>
<meta
name="msapplication-TileColor"
content="#da532c"
>
<meta
name="theme-color"
content="#ffffff"
>
<link rel="icon" type="image/svg+xml" href="/favicon/favicon.svg" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png" />
<link rel="manifest" href="/favicon/site.webmanifest" />
<link rel="mask-icon" href="/favicon/safari-pinned-tab.svg" color="#5bbad5" />
<meta name="msapplication-config" content="/favicon/browserconfig.xml" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<!-- Fonts -->
<link
rel="preconnect"
href="https://fonts.googleapis.com"
/>
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossorigin
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Manrope:wght@200..800&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap"
rel="stylesheet"
Expand All @@ -109,10 +43,6 @@

<body>
<div id="root"></div>
<script
type="module"
src="/src/main.tsx"
></script>
<script type="module" src="/src/main.tsx"></script>
</body>

</html>
</html>
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"dev": "vite",
"dev:watch": "routes:watch & vite",
"build": "tsc && vite build",
"build": "tsc --noEmit && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint --fix .",
"prettier:fix": "prettier --write --ignore-unknown .",
Expand All @@ -17,7 +17,6 @@
"test": "vitest run",
"test:coverage": "vitest run --coverage",
"test:watch": "vitest",
"type-check": "bash -c tsc --noEmit",
"wagmi-generate": "wagmi generate --config src/lib/wagmi/config.ts",
"postinstall": "pnpm wagmi-generate"
},
Expand All @@ -30,7 +29,7 @@
"prettier --write --ignore-unknown"
],
"src/**/*.{ts,tsx}": [
"pnpm type-check",
"tsc-files --noEmit",
"vitest related --run --coverage=false"
]
},
Expand Down Expand Up @@ -90,6 +89,7 @@
"prettier": "^3.3.2",
"stylelint": "^16.6.1",
"stylelint-config-standard": "^36.0.1",
"tsc-files": "^1.1.4",
"typescript": "^5.4.5",
"vite": "^5.2.11",
"vitest": "^1.6.0"
Expand Down
21 changes: 17 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export const env = createEnv({
PUBLIC_APP_NAME: z.string().min(1),
PUBLIC_APP_URL: z.string().optional(),
PUBLIC_INFURA_KEY: z.string().optional(),
PUBLIC_NATIVE_TOKEN_ADDRESS: z
.string()
.optional()
.default('0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'),
PUBLIC_RPC_ARBITRUM: z.string().optional(),
PUBLIC_RPC_ARBITRUM_SEPOLIA: z.string().optional(),
PUBLIC_RPC_BASE: z.string().optional(),
Expand Down
22 changes: 18 additions & 4 deletions src/hooks/useTokenSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Dispatch, SetStateAction, useDeferredValue, useState } from 'react'
import {
Dispatch,
SetStateAction,
useDeferredValue,
useState,
DependencyList,
useEffect,
} from 'react'

import { type Tokens } from '@/src/types/token'

Expand All @@ -13,13 +20,20 @@ type TokenSearch = {
* Internally it uses React's `useDeferredValue`
*
* @param {Array} tokens - a list of tokens to be filtered by `searchTerm`
* @returns {Array} List of Tokens
* @param {Array | undefined} deps - array of dependencies that trigger recalculation of the search
* @returns {TokenSearch} Object containing searchResult, searchTerm, and setSearchTerm
*/
export const useTokenSearch = (tokens: Tokens): TokenSearch => {
export const useTokenSearch = (tokens: Tokens, deps: DependencyList = []): TokenSearch => {
const [searchTerm, setSearchTerm] = useState('')
const [baseList] = useState(tokens)
const [baseList, setBaseList] = useState(tokens)
const deferredSearchTerm = useDeferredValue(searchTerm)

// update the baseList when deps changes
useEffect(() => {
setBaseList(tokens)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tokens, ...deps])

// if no searchTerm, return the unfiltered list
if (!deferredSearchTerm) {
return { searchResult: baseList, searchTerm, setSearchTerm }
Expand Down
33 changes: 32 additions & 1 deletion src/hooks/useTokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import {
type UseSuspenseQueryResult,
} from '@tanstack/react-query'
import defaultTokens from '@uniswap/default-token-list'
import * as chains from 'viem/chains'

import { tokenLists } from '@/src/constants/tokenLists'
import { env } from '@/src/env'
import { type Token, type Tokens, tokenSchema, type TokenList } from '@/src/types/token'
import { logger } from '@/src/utils/logger'

Expand Down Expand Up @@ -43,6 +45,7 @@ export const useTokens = ({
queryKey: ['tokens-list', url],
queryFn: () => fetchTokenList(url),
staleTime: Infinity,
gcTime: Infinity,
})),
combine: combineTokenLists,
})
Expand Down Expand Up @@ -90,7 +93,13 @@ function combineTokenLists(results: Array<UseSuspenseQueryResult<TokenList>>): T
acc.tokens.push(token)

if (!acc.tokensByChainId[token.chainId]) {
acc.tokensByChainId[token.chainId] = []
try {
// if there's a native token for the chain, add it to the list
acc.tokensByChainId[token.chainId] = [buildNativeToken(token.chainId)]
} catch (err) {
// if there's no native token for the chain, ignore the error
acc.tokensByChainId[token.chainId] = []
}
}

acc.tokensByChainId[token.chainId].push(token)
Expand Down Expand Up @@ -128,3 +137,25 @@ export async function fetchTokenList(url: string): Promise<TokenList> {

return result.json()
}

/**
* Builds a native token object based on the chain ID.
*
* @param chainId - The ID of the chain.
* @returns The native token object.
*/
function buildNativeToken(chainId: Token['chainId']): Token {
const tokenInfo = Object.values(chains).find((chain) => chain.id === chainId)?.nativeCurrency

if (!tokenInfo) {
throw new Error(`Native token not found for chain ID: ${chainId}`)
}

return {
name: tokenInfo.name,
address: env.PUBLIC_NATIVE_TOKEN_ADDRESS,
chainId: chainId,
decimals: tokenInfo.decimals,
symbol: tokenInfo.symbol,
}
}
11 changes: 9 additions & 2 deletions src/pageComponents/home/Examples/demos/TokenDropdown/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
import { type FC, useState } from 'react'

import { arbitrum, mainnet, polygon } from 'viem/chains'
import { arbitrum, mainnet, optimism, polygon } from 'viem/chains'

import Arbitrum from '@/src/pageComponents/home/Examples/demos/assets/Arbitrum'
import Eth from '@/src/pageComponents/home/Examples/demos/assets/Eth'
import Optimism from '@/src/pageComponents/home/Examples/demos/assets/Optimism'
import Polygon from '@/src/pageComponents/home/Examples/demos/assets/Polygon'
import TokenDropdown from '@/src/sharedComponents/TokenDropdown'
import { type Networks } from '@/src/sharedComponents/TokenSelect'
import { type Token } from '@/src/types/token'

const TokenDropdownDemo: FC = ({ ...restProps }) => {
const [currentNetworkId, setCurrentNetworkId] = useState<number>(mainnet.id)
const [currentToken, setCurrentToken] = useState<Token | undefined>()
const [currentToken, setCurrentToken] = useState<Token>()
const networks: Networks = [
{
icon: <Eth />,
id: mainnet.id,
label: mainnet.name,
onClick: () => setCurrentNetworkId(mainnet.id),
},
{
icon: <Optimism />,
id: optimism.id,
label: optimism.name,
onClick: () => setCurrentNetworkId(optimism.id),
},
{
icon: <Arbitrum />,
id: arbitrum.id,
Expand Down
Loading
Loading