1
0
Fork 0

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

View File

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