1
0
mirror of https://github.com/kremalicious/blog.git synced 2025-01-07 04:04:19 +01:00

refactor send

This commit is contained in:
Matthias Kretschmann 2023-11-02 03:22:04 +00:00
parent ec9e1ecdfd
commit 52dba93d71
Signed by: m
GPG Key ID: 606EEEF3C479A91F
13 changed files with 156 additions and 185 deletions

View File

@ -2,15 +2,12 @@ import { type ReactElement, useState, useEffect } from 'react'
import { useDebounce } from 'use-debounce' import { useDebounce } from 'use-debounce'
import { useAccount } from 'wagmi' import { useAccount } from 'wagmi'
import { ConnectButton } from '@rainbow-me/rainbowkit' import { ConnectButton } from '@rainbow-me/rainbowkit'
import Alert from '../Alert/Alert'
import { InputGroup } from '../Input' import { InputGroup } from '../Input'
import styles from './index.module.css' import styles from './index.module.css'
import { SendPrepareNative, SendPrepareErc20 } from '../SendPrepare'
import { useSend } from '../../hooks/useSend'
import type { SendFormData } from './types'
import { useStore } from '@nanostores/react' import { useStore } from '@nanostores/react'
import { $selectedToken } from '@features/Web3/stores/selectedToken' import { $selectedToken } from '@features/Web3/stores/selectedToken'
import siteConfig from '@config/blog.config' import siteConfig from '@config/blog.config'
import { Send } from '../Send/Send'
export default function Web3Form(): ReactElement { export default function Web3Form(): ReactElement {
const { address: account } = useAccount() const { address: account } = useAccount()
@ -18,10 +15,7 @@ export default function Web3Form(): ReactElement {
const [amount, setAmount] = useState('') const [amount, setAmount] = useState('')
const [debouncedAmount] = useDebounce(amount, 500) const [debouncedAmount] = useDebounce(amount, 500)
const [sendFormData, setSendFormData] = useState<SendFormData>() const [initSend, setInitSend] = useState(false)
const { data, send } = sendFormData || {}
const { message } = useSend(sendFormData)
const isDisabled = !account const isDisabled = !account
@ -30,45 +24,31 @@ export default function Web3Form(): ReactElement {
setAmount('') setAmount('')
}, [selectedToken]) }, [selectedToken])
return ( return initSend ? (
<Send amount={debouncedAmount} setInitSend={setInitSend} />
) : (
<form <form
className={styles.web3} className={styles.web3}
onSubmit={async (e) => { onSubmit={(e) => {
e.preventDefault() e.preventDefault()
if (!send || debouncedAmount === '' || debouncedAmount === '0') return if (debouncedAmount !== '' || debouncedAmount === '0') return
await send() setInitSend(true)
}} }}
> >
{message && message.status !== 'error' ? ( <>
<Alert message={message} transactionHash={data?.hash} /> <div className={styles.rainbowkit}>
) : ( <ConnectButton chainStatus="full" showBalance={false} />
<> </div>
<div className={styles.rainbowkit}> <InputGroup
<ConnectButton chainStatus="full" showBalance={false} /> amount={amount}
</div> setAmount={setAmount}
<InputGroup setInitSend={setInitSend}
amount={amount} isDisabled={isDisabled}
setAmount={setAmount}
isDisabled={isDisabled}
/>
<div className={styles.disclaimer}>
Sends tokens to my account{' '}
<code>{siteConfig.author.ether.ens}</code>
</div>
</>
)}
{selectedToken?.address === '0x0' ? (
<SendPrepareNative
amount={debouncedAmount}
setSendFormData={setSendFormData}
/> />
) : selectedToken ? ( <div className={styles.disclaimer}>
<SendPrepareErc20 Sends tokens to my account <code>{siteConfig.author.ether.ens}</code>
amount={debouncedAmount} </div>
setSendFormData={setSendFormData} </>
/>
) : null}
</form> </form>
) )
} }

View File

@ -7,11 +7,13 @@ import { TokenSelect } from '../TokenSelect'
export function InputGroup({ export function InputGroup({
amount, amount,
isDisabled, isDisabled,
setAmount setAmount,
setInitSend
}: { }: {
amount: string amount: string
isDisabled: boolean isDisabled: boolean
setAmount: React.Dispatch<React.SetStateAction<string>> setAmount: React.Dispatch<React.SetStateAction<string>>
setInitSend: React.Dispatch<React.SetStateAction<boolean>>
}): ReactElement { }): ReactElement {
return ( return (
<> <>
@ -34,6 +36,7 @@ export function InputGroup({
<button <button
className={`${styles.submit} btn btn-primary`} className={`${styles.submit} btn btn-primary`}
disabled={isDisabled || !amount} disabled={isDisabled || !amount}
onClick={() => setInitSend(true)}
> >
Make it rain Make it rain
</button> </button>

View File

@ -0,0 +1,74 @@
import { useStore } from '@nanostores/react'
import { $selectedToken } from '@features/Web3/stores/selectedToken'
import { useNetwork, useEnsAddress } from 'wagmi'
import siteConfig from '@config/blog.config'
import { useState, type FormEvent, useEffect } from 'react'
import { prepareTransaction } from './prepareTransaction'
import { sendTransaction } from './sendTransaction'
import type {
SendTransactionArgs,
WriteContractPreparedArgs
} from 'wagmi/actions'
import { formatEther } from 'viem'
export function Send({
amount,
setInitSend
}: {
amount: string
setInitSend: (initSend: boolean) => void
}) {
const { chain } = useNetwork()
const selectedToken = useStore($selectedToken)
const { data: to } = useEnsAddress({
name: siteConfig.author.ether.ens,
chainId: 1
})
const [txConfig, setTxConfig] = useState<
SendTransactionArgs | WriteContractPreparedArgs
>()
const [txHash, setTxHash] = useState<string>()
useEffect(() => {
async function init() {
if (!selectedToken || !amount || !to || !chain?.id) return
const config = await prepareTransaction(
selectedToken,
amount,
to,
chain.id
)
setTxConfig(config)
}
init()
}, [selectedToken || amount || to || chain?.id])
async function handleSend(event: FormEvent<HTMLButtonElement>) {
event?.preventDefault()
const result = await sendTransaction(selectedToken, txConfig)
setTxHash(result?.hash)
}
const value =
(txConfig as SendTransactionArgs)?.value ||
(txConfig as WriteContractPreparedArgs)?.request?.args[1] ||
'0'
const displayAmountFromConfig = formatEther(value)
console.log(txHash)
return (
<>
<div>
<p>You are about to send</p>
<p>
{displayAmountFromConfig} {selectedToken?.symbol} to <code>{to}</code>{' '}
on {chain?.name}
</p>
<button onClick={(e) => handleSend(e)}>Confirm</button>
<button onClick={() => setInitSend(false)}>Cancel</button>
</div>
</>
)
}

View File

@ -0,0 +1 @@
export * from './Send'

View File

@ -0,0 +1,35 @@
import type { GetToken } from '@features/Web3/stores/tokens'
import { parseEther, parseUnits } from 'viem'
import {
prepareSendTransaction,
prepareWriteContract,
type SendTransactionArgs,
type WriteContractPreparedArgs
} from 'wagmi/actions'
import { abiErc20Transfer } from './abiErc20Transfer'
export async function prepareTransaction(
selectedToken: GetToken | undefined,
amount: string | undefined,
to: `0x${string}` | null | undefined,
chainId: number | undefined
) {
if (!chainId || !to || !amount || !selectedToken) return
const isNative = selectedToken?.address === '0x0'
const config = isNative
? ((await prepareSendTransaction({
chainId,
to,
value: parseEther(amount)
})) as SendTransactionArgs)
: ((await prepareWriteContract({
address: selectedToken?.address,
abi: abiErc20Transfer,
functionName: 'transfer',
args: [to, parseUnits(amount, selectedToken?.decimals || 18)]
})) as WriteContractPreparedArgs)
return config
}

View File

@ -0,0 +1,21 @@
import type { GetToken } from '@features/Web3/stores/tokens'
import {
sendTransaction as sendNative,
writeContract,
type SendTransactionArgs,
type WriteContractPreparedArgs
} from 'wagmi/actions'
export async function sendTransaction(
selectedToken: GetToken | undefined,
config: SendTransactionArgs | WriteContractPreparedArgs | undefined
) {
if (!config || !selectedToken) return
const result =
selectedToken?.address === '0x0'
? await sendNative(config as SendTransactionArgs)
: await writeContract(config as WriteContractPreparedArgs)
return result
}

View File

@ -1,50 +0,0 @@
import { parseUnits } from 'viem'
import { useContractWrite, useEnsAddress, usePrepareContractWrite } from 'wagmi'
import siteConfig from '@config/blog.config'
import { abiErc20Transfer } from './abiErc20Transfer'
import { useEffect } from 'react'
import { useStore } from '@nanostores/react'
import { $selectedToken } from '@features/Web3/stores/selectedToken'
export function SendPrepareErc20({
amount,
setSendFormData
}: {
amount: string
setSendFormData: any
}) {
const selectedToken = useStore($selectedToken)
const { data: to } = useEnsAddress({
name: siteConfig.author.ether.ens,
chainId: 1
})
const { config } = usePrepareContractWrite({
address: selectedToken?.address,
abi: abiErc20Transfer,
functionName: 'transfer',
args: [to || undefined, parseUnits(amount, selectedToken?.decimals || 18)]
})
const {
data,
writeAsync: send,
isError,
isSuccess,
isLoading,
error
} = useContractWrite(config)
useEffect(() => {
setSendFormData({
data,
send,
isError,
isSuccess,
isLoading,
error
})
}, [data, send, isError, isSuccess, isLoading, error])
return <></>
}

View File

@ -1,49 +0,0 @@
import { parseEther } from 'viem'
import {
useEnsAddress,
useNetwork,
usePrepareSendTransaction,
useSendTransaction
} from 'wagmi'
import siteConfig from '@config/blog.config'
import { useEffect } from 'react'
export function SendPrepareNative({
amount,
setSendFormData
}: {
amount: string
setSendFormData: any
}) {
const { chain } = useNetwork()
const { data: to } = useEnsAddress({
name: siteConfig.author.ether.ens,
chainId: 1
})
const { config } = usePrepareSendTransaction({
chainId: chain?.id,
to: to || undefined,
value: parseEther(amount)
})
const {
data,
sendTransactionAsync: send,
isError,
isSuccess,
isLoading,
error
} = useSendTransaction(config)
useEffect(() => {
setSendFormData({
data,
send,
isError,
isSuccess,
isLoading,
error
})
}, [data, send, isError, isSuccess, isLoading, error])
return <></>
}

View File

@ -1,2 +0,0 @@
export * from './SendErc20'
export * from './SendNative'

View File

@ -1 +0,0 @@
export * from './useSend'

View File

@ -1,41 +0,0 @@
import { getTransactionMessage } from '@features/Web3/components/Alert/Alert'
import type { SendFormData } from '@features/Web3/components/Form/types'
import { useEffect, useState } from 'react'
export function useSend(sendFormData: SendFormData | undefined) {
const { isLoading, isSuccess, isError, error } = sendFormData || {}
const [message, setMessage] = useState<{ status: string; text: string }>()
useEffect(() => {
if (!isError || !error) return
setMessage(
error.message.includes('User rejected the request.')
? undefined
: {
status: 'error',
text: error?.message as string
}
)
}, [isError])
useEffect(() => {
if (!isLoading) return
setMessage({
status: 'loading',
text: getTransactionMessage().waitingConfirmation
})
}, [isLoading])
useEffect(() => {
if (!isSuccess) return
setMessage({
status: 'success',
text: getTransactionMessage().success
})
}, [isSuccess])
return { message }
}