-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: swap controls for the example app (#148)
* restructure code a little * add invariant * add inputs for both eth and weth * add controls * remove console log
- Loading branch information
1 parent
dbb71a8
commit bb84614
Showing
9 changed files
with
296 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { ConnectKitButton } from 'connectkit' | ||
import { PropsWithChildren } from 'react' | ||
import { ClientOnly } from 'remix-utils/client-only' | ||
import { useAccount, useDisconnect } from 'wagmi' | ||
|
||
export const Connect = () => { | ||
const account = useAccount() | ||
const { disconnect } = useDisconnect() | ||
|
||
if (account.isDisconnected || account.isConnecting) { | ||
return <ConnectKitButton /> | ||
} | ||
|
||
return ( | ||
<ClientOnly> | ||
{() => ( | ||
<button | ||
className="rounded bg-red-500 px-4 py-2 text-white outline-none transition-colors hover:bg-red-600" | ||
onClick={() => disconnect()} | ||
> | ||
Disconnect wallet | ||
</button> | ||
)} | ||
</ClientOnly> | ||
) | ||
} | ||
|
||
export const Connected = ({ children }: PropsWithChildren) => { | ||
const account = useAccount() | ||
|
||
if (account.isDisconnected || account.isConnecting) { | ||
return null | ||
} | ||
|
||
return <ClientOnly>{() => children}</ClientOnly> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { AllHTMLAttributes, useId } from 'react' | ||
|
||
type InputProps = AllHTMLAttributes<HTMLInputElement> & { | ||
label: string | ||
} | ||
|
||
export const Input = ({ label, children, ...props }: InputProps) => { | ||
const id = useId() | ||
|
||
return ( | ||
<div className="flex flex-col gap-2"> | ||
<label htmlFor={id} className="ml-4 font-semibold"> | ||
{label} | ||
</label> | ||
|
||
<input | ||
{...props} | ||
id={id} | ||
className="w-full rounded border border-gray-200 bg-gray-100 px-4 py-2 outline-none ring-2 ring-transparent focus:border-blue-600 focus:ring-blue-300" | ||
/> | ||
|
||
{children && <div className="ml-4">{children}</div>} | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { Input } from './Input' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,106 @@ | ||
import { formatEther } from 'viem' | ||
import { useBalance } from 'wagmi' | ||
import { PropsWithChildren } from 'react' | ||
import { Abi, Address, erc20Abi, formatEther, formatUnits } from 'viem' | ||
import { useBalance, useReadContract } from 'wagmi' | ||
|
||
type BalanceProps = { | ||
contract: `0x${string}` | ||
address: Address | ||
contract?: Address | ||
} | ||
|
||
export const Balance = ({ contract }: BalanceProps) => { | ||
const { data, isPending } = useBalance({ address: contract }) | ||
export const Balance = ({ address, contract }: BalanceProps) => { | ||
return ( | ||
<div className="flex items-center gap-2 text-xs leading-none text-gray-500"> | ||
<span className="font-semibold uppercase">Balance</span> | ||
{contract ? ( | ||
<ER20Balance address={address} contract={contract} /> | ||
) : ( | ||
<EthBalance address={address} /> | ||
)} | ||
</div> | ||
) | ||
} | ||
|
||
if (isPending) { | ||
return null | ||
const useEthBalance = (address: Address): BalanceValue | [null, null] => { | ||
const { data, isFetched } = useBalance({ address }) | ||
|
||
if (isFetched) { | ||
return [formatEther(data.value), data.symbol] as const | ||
} | ||
|
||
return [null, null] | ||
} | ||
|
||
const Symbol = ({ children }: PropsWithChildren) => ( | ||
<span className="rounded bg-blue-100 px-1 text-xs font-semibold uppercase tabular-nums text-blue-500"> | ||
{children} | ||
</span> | ||
) | ||
|
||
type BalanceValue = [balance: string, symbol: string] | ||
|
||
type EthBalanceProps = { | ||
address: Address | ||
} | ||
|
||
const EthBalance = ({ address }: EthBalanceProps) => { | ||
const [balance, symbol] = useEthBalance(address) | ||
|
||
return ( | ||
<div className="flex items-center gap-2"> | ||
{formatEther(data.value)} | ||
<> | ||
{balance} | ||
|
||
<span className="rounded bg-blue-100 px-1 text-xs font-semibold uppercase tabular-nums text-blue-500"> | ||
{data.symbol} | ||
</span> | ||
</div> | ||
<Symbol>{symbol}</Symbol> | ||
</> | ||
) | ||
} | ||
|
||
type UseER20BalanceOptions<T extends Abi> = { | ||
address: Address | ||
contract: Address | ||
} | ||
|
||
const useERC20Balance = <T extends Abi>({ | ||
address, | ||
contract, | ||
}: UseER20BalanceOptions<T>): BalanceValue | [null, null] => { | ||
const balanceOf = useReadContract({ | ||
abi: erc20Abi, | ||
functionName: 'balanceOf', | ||
address: contract, | ||
args: [address], | ||
}) | ||
|
||
const decimals = useReadContract({ | ||
abi: erc20Abi, | ||
functionName: 'decimals', | ||
address: contract, | ||
}) | ||
|
||
const symbol = useReadContract({ | ||
abi: erc20Abi, | ||
functionName: 'symbol', | ||
address: contract, | ||
}) | ||
|
||
if (balanceOf.isFetched && decimals.isFetched && symbol.isFetched) { | ||
return [formatUnits(balanceOf.data, decimals.data), symbol.data] as const | ||
} | ||
|
||
return [null, null] | ||
} | ||
|
||
type ER20BalanceProps = { | ||
address: Address | ||
contract: Address | ||
} | ||
|
||
const ER20Balance = ({ address, contract }: ER20BalanceProps) => { | ||
const [balance, symbol] = useERC20Balance({ address, contract }) | ||
|
||
return ( | ||
<> | ||
{balance} | ||
<Symbol>{symbol}</Symbol> | ||
</> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,89 @@ | ||
import { ConnectKitButton } from 'connectkit' | ||
import { Input } from '@/components' | ||
import { ChevronLeft, ChevronRight } from 'lucide-react' | ||
import { useState } from 'react' | ||
import { Fragment } from 'react/jsx-runtime' | ||
import { useAccount, useDisconnect } from 'wagmi' | ||
import { useAccount } from 'wagmi' | ||
import { Balance } from './Balance' | ||
|
||
type Target = 'ETH' | 'WETH' | ||
|
||
export const Transfer = () => { | ||
const account = useAccount() | ||
const { disconnect } = useDisconnect() | ||
|
||
if (account.isDisconnected || account.isConnecting) { | ||
return <ConnectKitButton /> | ||
} | ||
const [target, setTarget] = useState<Target>('WETH') | ||
|
||
return ( | ||
<dl> | ||
<form | ||
className="flex w-1/3 flex-col gap-8" | ||
onSubmit={(event) => { | ||
event.preventDefault() | ||
}} | ||
> | ||
{account.addresses.map((address) => ( | ||
<Fragment key={address}> | ||
<dt className="font-semibold">Account</dt> | ||
<dd className="flex items-center gap-2"> | ||
{account.address} | ||
|
||
<button | ||
className="rounded bg-red-50 px-2 text-red-500 outline-none transition-colors hover:bg-red-100" | ||
onClick={() => disconnect()} | ||
> | ||
Disconnect | ||
</button> | ||
</dd> | ||
|
||
<dt className="font-semibold">Balance</dt> | ||
<dd> | ||
<Balance contract={account.address} /> | ||
</dd> | ||
<Input disabled defaultValue={address} label="Account" /> | ||
|
||
<div className="grid grid-cols-5 gap-8"> | ||
<div className="col-span-2"> | ||
<Input | ||
disabled={target === 'ETH'} | ||
required={target === 'WETH'} | ||
step="0.000000000000000001" | ||
label="ETH" | ||
name="eth" | ||
placeholder="0" | ||
type="number" | ||
> | ||
<Balance address={address} /> | ||
</Input> | ||
</div> | ||
|
||
<div className="flex items-center justify-center"> | ||
<button | ||
type="button" | ||
className="rounded p-2 hover:bg-gray-100" | ||
onClick={() => setTarget(target === 'ETH' ? 'WETH' : 'ETH')} | ||
> | ||
{target === 'WETH' ? ( | ||
<> | ||
<span className="sr-only">Swap WETH to ETH</span> | ||
<ChevronRight /> | ||
</> | ||
) : ( | ||
<> | ||
<span className="sr-only">Swap ETH to WETH</span> | ||
<ChevronLeft /> | ||
</> | ||
)} | ||
</button> | ||
</div> | ||
|
||
<div className="col-span-2"> | ||
<Input | ||
disabled={target === 'WETH'} | ||
required={target === 'ETH'} | ||
step="0.000000000000000001" | ||
label="WETH" | ||
name="weth" | ||
placeholder="0" | ||
type="number" | ||
> | ||
<Balance | ||
address={address} | ||
contract="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" | ||
/> | ||
</Input> | ||
</div> | ||
</div> | ||
|
||
<button | ||
type="submit" | ||
className="rounded border border-transparent bg-gray-900 px-4 py-2 font-semibold text-white outline-none ring-2 ring-transparent hover:bg-gray-800 focus:border-purple-700 focus:ring-purple-400" | ||
> | ||
Transfer | ||
</button> | ||
</Fragment> | ||
))} | ||
</dl> | ||
</form> | ||
) | ||
} |
Oops, something went wrong.