mirror of
https://github.com/kremalicious/blog.git
synced 2025-01-03 10:25:07 +01:00
refactor send
This commit is contained in:
parent
ec9e1ecdfd
commit
52dba93d71
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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>
|
||||
|
74
src/features/Web3/components/Send/Send.tsx
Normal file
74
src/features/Web3/components/Send/Send.tsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
1
src/features/Web3/components/Send/index.tsx
Normal file
1
src/features/Web3/components/Send/index.tsx
Normal file
@ -0,0 +1 @@
|
||||
export * from './Send'
|
35
src/features/Web3/components/Send/prepareTransaction.ts
Normal file
35
src/features/Web3/components/Send/prepareTransaction.ts
Normal 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
|
||||
}
|
21
src/features/Web3/components/Send/sendTransaction.ts
Normal file
21
src/features/Web3/components/Send/sendTransaction.ts
Normal 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
|
||||
}
|
@ -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 <></>
|
||||
}
|
@ -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 <></>
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
export * from './SendErc20'
|
||||
export * from './SendNative'
|
@ -1 +0,0 @@
|
||||
export * from './useSend'
|
@ -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 }
|
||||
}
|
Loading…
Reference in New Issue
Block a user