mirror of
https://github.com/kremalicious/blog.git
synced 2024-11-22 01:46:51 +01:00
automatic network switching
This commit is contained in:
parent
d1315e7912
commit
cbbb81e391
@ -32,8 +32,15 @@ table[aria-disabled='true'] {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table :global(.TokenLogo),
|
.table :global(.TokenLogo) {
|
||||||
.table :global(.TokenLogo) img {
|
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table :global(.TokenLogo) img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useAccount, useEnsName } from 'wagmi'
|
import { useAccount, useChains, useEnsName } from 'wagmi'
|
||||||
import styles from './Data.module.css'
|
import styles from './Data.module.css'
|
||||||
import { useStore } from '@nanostores/react'
|
import { useStore } from '@nanostores/react'
|
||||||
import { $amount, $selectedToken } from '@features/Web3/stores'
|
import { $amount, $selectedToken } from '@features/Web3/stores'
|
||||||
@ -13,11 +13,17 @@ export function Data({
|
|||||||
ensResolved: string | null | undefined
|
ensResolved: string | null | undefined
|
||||||
isDisabled: boolean
|
isDisabled: boolean
|
||||||
}) {
|
}) {
|
||||||
const { address: from, chain } = useAccount()
|
const chains = useChains()
|
||||||
|
const { address: from } = useAccount()
|
||||||
const { data: ensFrom } = useEnsName({ address: from, chainId: 1 })
|
const { data: ensFrom } = useEnsName({ address: from, chainId: 1 })
|
||||||
|
|
||||||
const selectedToken = useStore($selectedToken)
|
const selectedToken = useStore($selectedToken)
|
||||||
const amount = useStore($amount)
|
const amount = useStore($amount)
|
||||||
|
|
||||||
|
const networkName = chains.filter(
|
||||||
|
(chain) => chain.id === selectedToken?.chainId
|
||||||
|
)[0].name
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table className={styles.table} aria-disabled={isDisabled}>
|
<table className={styles.table} aria-disabled={isDisabled}>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -54,7 +60,10 @@ export function Data({
|
|||||||
<tr>
|
<tr>
|
||||||
<td className={styles.label}>on</td>
|
<td className={styles.label}>on</td>
|
||||||
<td>
|
<td>
|
||||||
<span className={styles.network}>{chain?.name}</span>
|
<div className="TokenLogo">
|
||||||
|
<img src={selectedToken?.chainLogo || ''} />
|
||||||
|
</div>
|
||||||
|
<span className={styles.network}>{networkName}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
@ -18,12 +18,6 @@ export function Preview() {
|
|||||||
|
|
||||||
const { handleSend, isLoading, error } = useSend()
|
const { handleSend, isLoading, error } = useSend()
|
||||||
|
|
||||||
// TODO: Cancel flow if chain changes in preview as this can mess with token selection
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (!chain?.id || $isInitSend.get() === false) return
|
|
||||||
// $isInitSend.set(false)
|
|
||||||
// }, [chain?.id])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Data to={to} ensResolved={ensResolved} isDisabled={isLoading} />
|
<Data to={to} ensResolved={ensResolved} isDisabled={isLoading} />
|
||||||
|
@ -3,18 +3,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rainbowkit > div:first-child {
|
.rainbowkit > div:first-child {
|
||||||
display: flex;
|
/* display: flex;
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
justify-content: space-between;
|
justify-content: space-between; */
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* hide the account icon, and hope nothing else */
|
|
||||||
|
|
||||||
/* .rainbowkit button [aria-hidden] {
|
|
||||||
display: none;
|
|
||||||
} */
|
|
||||||
|
|
||||||
.rainbowkit [aria-label='Chain Selector'],
|
.rainbowkit [aria-label='Chain Selector'],
|
||||||
.rainbowkit [data-testid='rk-account-button'] div {
|
.rainbowkit [data-testid='rk-account-button'] div {
|
||||||
font-weight: var(--font-weight-base);
|
font-weight: var(--font-weight-base);
|
||||||
|
@ -4,7 +4,7 @@ import styles from './RainbowKit.module.css'
|
|||||||
export function RainbowKit() {
|
export function RainbowKit() {
|
||||||
return (
|
return (
|
||||||
<div className={styles.rainbowkit}>
|
<div className={styles.rainbowkit}>
|
||||||
<ConnectButton chainStatus="full" showBalance={false} />
|
<ConnectButton chainStatus="none" showBalance={false} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.TokenLogo {
|
.TokenLogo {
|
||||||
width: 28px;
|
width: 32px;
|
||||||
height: 28px;
|
height: 32px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: calc(var(--spacer) / 4);
|
margin-right: calc(var(--spacer) / 4);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
@ -30,15 +30,24 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.TokenLogo img {
|
.TokenLogoImage {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 26px;
|
|
||||||
height: 26px;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.TokenChainLogo {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -5px;
|
||||||
|
right: -5px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
.TokenName,
|
.TokenName,
|
||||||
.TokenBalance {
|
.TokenBalance {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -41,10 +41,21 @@ export const Token = forwardRef<HTMLDivElement, SelectItemProps>(
|
|||||||
<Select.ItemText>
|
<Select.ItemText>
|
||||||
<span className="TokenLogo">
|
<span className="TokenLogo">
|
||||||
{token?.logo ? (
|
{token?.logo ? (
|
||||||
<img src={token.logo} width="32" height="32" />
|
<img
|
||||||
|
src={token.logo}
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
className="TokenLogoImage"
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
token?.symbol?.substring(0, 3)
|
token?.symbol?.substring(0, 3)
|
||||||
)}
|
)}
|
||||||
|
<img
|
||||||
|
src={token?.chainLogo}
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
className="TokenChainLogo"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</Select.ItemText>
|
</Select.ItemText>
|
||||||
<div>
|
<div>
|
||||||
|
@ -17,7 +17,7 @@ export function TokenSelect() {
|
|||||||
const selectedToken = useStore($selectedToken)
|
const selectedToken = useStore($selectedToken)
|
||||||
|
|
||||||
const items = tokens?.map((token) => (
|
const items = tokens?.map((token) => (
|
||||||
<Token key={token.address} token={token} />
|
<Token key={`${token.address}-${token.chainId}`} token={token} />
|
||||||
))
|
))
|
||||||
|
|
||||||
function handleValueChange(value: `0x${string}`) {
|
function handleValueChange(value: `0x${string}`) {
|
||||||
@ -32,12 +32,12 @@ export function TokenSelect() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedToken?.address || !tokens || !tokens?.length) return
|
if (selectedToken?.address || !tokens || !tokens?.length) return
|
||||||
|
|
||||||
handleValueChange('0x0')
|
// select ETH mainnet token
|
||||||
|
handleValueChange('0x0-1')
|
||||||
}, [tokens, selectedToken])
|
}, [tokens, selectedToken])
|
||||||
|
|
||||||
return tokens && address ? (
|
return tokens && address ? (
|
||||||
<Select.Root
|
<Select.Root
|
||||||
// defaultValue={selectedToken?.address || tokens[0].address}
|
|
||||||
value={selectedToken?.address}
|
value={selectedToken?.address}
|
||||||
onValueChange={(value: `0x${string}`) => handleValueChange(value)}
|
onValueChange={(value: `0x${string}`) => handleValueChange(value)}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
|
@ -2,12 +2,13 @@ export type GetToken = {
|
|||||||
address: `0x${string}`
|
address: `0x${string}`
|
||||||
balance: number | undefined
|
balance: number | undefined
|
||||||
chainId: number
|
chainId: number
|
||||||
name: string | null
|
chainLogo: string | undefined
|
||||||
symbol: string | null
|
name: string | undefined
|
||||||
decimals: number | null
|
symbol: string | undefined
|
||||||
logo: string | null
|
decimals: number | undefined
|
||||||
|
logo: string | undefined
|
||||||
price: {
|
price: {
|
||||||
usd: number | null
|
usd: number | undefined
|
||||||
eur: number | null
|
eur: number | undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import useSWR, { type SWRResponse } from 'swr'
|
import useSWR, { type SWRResponse } from 'swr'
|
||||||
import { useChainId, useAccount } from 'wagmi'
|
import { useAccount, useChains } from 'wagmi'
|
||||||
import type { GetToken } from './types'
|
import type { GetToken } from './types'
|
||||||
|
|
||||||
const fetcher = (url: string) => fetch(url).then((res) => res.json())
|
const fetcher = (url: string) => fetch(url).then((res) => res.json())
|
||||||
@ -10,26 +10,23 @@ const apiUrl = import.meta.env.PUBLIC_WEB3_API_URL
|
|||||||
// Wrapper for fetching user tokens with swr.
|
// Wrapper for fetching user tokens with swr.
|
||||||
//
|
//
|
||||||
export function useFetchTokens(): SWRResponse<GetToken[] | undefined, Error> {
|
export function useFetchTokens(): SWRResponse<GetToken[] | undefined, Error> {
|
||||||
const chainId = useChainId()
|
|
||||||
const { address } = useAccount()
|
const { address } = useAccount()
|
||||||
// const { chains } = useConfig()
|
const chains = useChains()
|
||||||
|
|
||||||
const [url, setUrl] = useState<string | undefined>()
|
const [url, setUrl] = useState<string | undefined>()
|
||||||
|
|
||||||
const fetchResults = useSWR<GetToken[] | undefined>(url, fetcher)
|
const fetchResults = useSWR<GetToken[] | undefined>(url, fetcher)
|
||||||
|
|
||||||
// Set url only after we have all data loaded on client,
|
// Set url only after we have all data loaded on client,
|
||||||
// preventing initial fetch.
|
// preventing initial fetch.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!address || !chainId) {
|
if (!address || !chains) {
|
||||||
setUrl(undefined)
|
setUrl(undefined)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// const chainIds = chains.map((chain) => chain.id).join(',')
|
const chainIds = chains.map((chain) => chain.id).join(',')
|
||||||
const url = `${apiUrl}/balance?address=${address}&chainIds=${chainId}`
|
const url = `${apiUrl}/balance?address=${address}&chainIds=${chainIds}`
|
||||||
setUrl(url)
|
setUrl(url)
|
||||||
}, [address, chainId])
|
}, [address, chains])
|
||||||
|
|
||||||
return fetchResults
|
return fetchResults
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ export async function send(
|
|||||||
) {
|
) {
|
||||||
if (!selectedToken?.decimals || !amount || !to) return
|
if (!selectedToken?.decimals || !amount || !to) return
|
||||||
|
|
||||||
const isNative = selectedToken.address === '0x0'
|
const isNative = selectedToken.address.startsWith('0x0')
|
||||||
const requestNative = { chainId, to, value: parseEther(amount) }
|
const requestNative = { chainId, to, value: parseEther(amount) }
|
||||||
const requestErc20 = {
|
const requestErc20 = {
|
||||||
chainId,
|
chainId,
|
||||||
|
@ -3,7 +3,7 @@ import { useStore } from '@nanostores/react'
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { send } from './send'
|
import { send } from './send'
|
||||||
import { isUnhelpfulErrorMessage } from './isUnhelpfulErrorMessage'
|
import { isUnhelpfulErrorMessage } from './isUnhelpfulErrorMessage'
|
||||||
import { useAccount, useConfig, useEnsAddress } from 'wagmi'
|
import { useAccount, useConfig, useEnsAddress, useSwitchChain } from 'wagmi'
|
||||||
import siteConfig from '@config/blog.config'
|
import siteConfig from '@config/blog.config'
|
||||||
|
|
||||||
export function useSend() {
|
export function useSend() {
|
||||||
@ -13,17 +13,31 @@ export function useSend() {
|
|||||||
const { chainId } = useAccount()
|
const { chainId } = useAccount()
|
||||||
const { ens } = siteConfig.author.ether
|
const { ens } = siteConfig.author.ether
|
||||||
const { data: to } = useEnsAddress({ name: ens, chainId: 1 })
|
const { data: to } = useEnsAddress({ name: ens, chainId: 1 })
|
||||||
|
const { switchChain } = useSwitchChain()
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [isError, setIsError] = useState(false)
|
const [isError, setIsError] = useState(false)
|
||||||
const [error, setError] = useState<string | undefined>()
|
const [error, setError] = useState<string | undefined>()
|
||||||
|
|
||||||
async function handleSend() {
|
async function handleSend() {
|
||||||
|
if (!selectedToken || !amount || !to) return
|
||||||
|
|
||||||
|
// switch chains first
|
||||||
|
if (chainId !== selectedToken.chainId) {
|
||||||
|
switchChain({ chainId: selectedToken.chainId })
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIsError(false)
|
setIsError(false)
|
||||||
setError(undefined)
|
setError(undefined)
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const hash = await send(config, selectedToken, amount, to, chainId)
|
const hash = await send(
|
||||||
|
config,
|
||||||
|
selectedToken,
|
||||||
|
amount,
|
||||||
|
to,
|
||||||
|
selectedToken.chainId
|
||||||
|
)
|
||||||
if (hash) $txHash.set(hash)
|
if (hash) $txHash.set(hash)
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const errorMessage = (error as Error).message
|
const errorMessage = (error as Error).message
|
||||||
|
Loading…
Reference in New Issue
Block a user