mirror of
https://github.com/kremalicious/blog.git
synced 2025-01-07 04:04:19 +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 { 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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
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