Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Icon from '@/src/components/pageComponents/home/Examples/demos/OptimismCr
import Wrapper from '@/src/components/pageComponents/home/Examples/wrapper'
import Hash from '@/src/components/sharedComponents/Hash'
import TransactionButton from '@/src/components/sharedComponents/TransactionButton'
import { withWalletStatusVerifier } from '@/src/components/sharedComponents/WalletStatusVerifier'
import { WalletStatusVerifier } from '@/src/components/sharedComponents/WalletStatusVerifier'
import { getContract } from '@/src/constants/contracts/contracts'
import { useL1CrossDomainMessengerProxy } from '@/src/hooks/useOPL1CrossDomainMessengerProxy'
import { useWeb3StatusConnected } from '@/src/hooks/useWeb3Status'
Expand All @@ -15,27 +15,27 @@ import { parseEther } from 'viem'
import { optimismSepolia, sepolia } from 'viem/chains'
import { extractTransactionDepositedLogs, getL2TransactionHash } from 'viem/op-stack'

const OptimismCrossDomainMessenger = withWalletStatusVerifier(
withSuspenseAndRetry(() => {
// https://sepolia-optimism.etherscan.io/address/0xb50201558b00496a145fe76f7424749556e326d8
const AAVEProxy = '0xb50201558b00496a145fe76f7424749556e326d8'
const { address: walletAddress, readOnlyClient } = useWeb3StatusConnected()
const OptimismCrossDomainMessenger = withSuspenseAndRetry(() => {
// https://sepolia-optimism.etherscan.io/address/0xb50201558b00496a145fe76f7424749556e326d8
const AAVEProxy = '0xb50201558b00496a145fe76f7424749556e326d8'
const { address: walletAddress, readOnlyClient } = useWeb3StatusConnected()

const contract = getContract('AAVEWeth', optimismSepolia.id)
const depositValue = parseEther('0.01')
const contract = getContract('AAVEWeth', optimismSepolia.id)
const depositValue = parseEther('0.01')

const [l2Hash, setL2Hash] = useState<Address | null>(null)
const [l2Hash, setL2Hash] = useState<Address | null>(null)

const sendCrossChainMessage = useL1CrossDomainMessengerProxy({
fromChain: sepolia,
contractName: 'AAVEWeth',
functionName: 'depositETH',
l2ContractAddress: contract.address,
args: [AAVEProxy, walletAddress, 0],
value: depositValue,
})
const sendCrossChainMessage = useL1CrossDomainMessengerProxy({
fromChain: sepolia,
contractName: 'AAVEWeth',
functionName: 'depositETH',
l2ContractAddress: contract.address,
args: [AAVEProxy, walletAddress, 0],
value: depositValue,
})

return (
return (
<WalletStatusVerifier chainId={sepolia.id}>
<Wrapper title="Execute transaction">
<p>
Deposit <b>0.01</b> ETH in{' '}
Expand Down Expand Up @@ -76,10 +76,9 @@ const OptimismCrossDomainMessenger = withWalletStatusVerifier(
</Flex>
)}
</Wrapper>
)
}),
{ chainId: sepolia.id },
)
</WalletStatusVerifier>
)
})

const optimismCrossdomainMessenger = {
demo: <OptimismCrossDomainMessenger />,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import BaseERC20ApproveAndTransferButton from '@/src/components/pageComponents/home/Examples/demos/TransactionButton/ERC20ApproveAndTransferButton/ERC20ApproveAndTransferButton'
import MintUSDC from '@/src/components/pageComponents/home/Examples/demos/TransactionButton/ERC20ApproveAndTransferButton/MintUSDC'
import Wrapper from '@/src/components/pageComponents/home/Examples/demos/TransactionButton/Wrapper'
import { withWalletStatusVerifier } from '@/src/components/sharedComponents/WalletStatusVerifier'
import { WalletStatusVerifier } from '@/src/components/sharedComponents/WalletStatusVerifier'
import { useSuspenseReadErc20BalanceOf } from '@/src/hooks/generated'
import { useWeb3StatusConnected } from '@/src/hooks/useWeb3Status'
import type { Token } from '@/src/types/token'
Expand Down Expand Up @@ -57,59 +57,56 @@ const ABIExample = [
*
* Works only on Sepolia chain.
*/
const ERC20ApproveAndTransferButton = withWalletStatusVerifier(
withSuspense(() => {
const { address } = useWeb3StatusConnected()
const { writeContractAsync } = useWriteContract()
const ERC20ApproveAndTransferButton = withSuspense(() => {
const { address } = useWeb3StatusConnected()
const { writeContractAsync } = useWriteContract()

const { data: balance, refetch: refetchBalance } = useSuspenseReadErc20BalanceOf({
address: tokenUSDC_sepolia.address as Address,
args: [address],
})
const { data: balance, refetch: refetchBalance } = useSuspenseReadErc20BalanceOf({
address: tokenUSDC_sepolia.address as Address,
args: [address],
})

// AAVE staging contract pool address
const spender = '0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951'
// AAVE staging contract pool address
const spender = '0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951'

const amount = 10000000000n // 10,000.00 USDC
const amount = 10000000000n // 10,000.00 USDC

const handleTransaction = () =>
writeContractAsync({
abi: ABIExample,
address: spender,
functionName: 'supply',
args: [tokenUSDC_sepolia.address as Address, amount, address, 0],
})
handleTransaction.methodId = 'Supply USDC'
const handleTransaction = () =>
writeContractAsync({
abi: ABIExample,
address: spender,
functionName: 'supply',
args: [tokenUSDC_sepolia.address as Address, amount, address, 0],
})
handleTransaction.methodId = 'Supply USDC'

const formattedAmount = formatNumberOrString(
formatUnits(amount, tokenUSDC_sepolia.decimals),
NumberType.TokenTx,
)
const formattedAmount = formatNumberOrString(
formatUnits(amount, tokenUSDC_sepolia.decimals),
NumberType.TokenTx,
)

return (
<>
{balance < amount ? (
<Wrapper
text={'Get Sepolia USDC from Aave faucet'}
title={'Mint USDC'}
>
<MintUSDC onSuccess={refetchBalance} />
</Wrapper>
) : (
<BaseERC20ApproveAndTransferButton
amount={amount}
label={`Supply ${formattedAmount} USDC`}
labelSending="Sending..."
onSuccess={() => refetchBalance}
spender={spender}
token={tokenUSDC_sepolia}
transaction={handleTransaction}
/>
)}
</>
)
}),
{ chainId: sepolia.id }, // this DEMO component only works on sepolia chain
)
return (
<WalletStatusVerifier chainId={sepolia.id}>
{balance < amount ? (
<Wrapper
text={'Get Sepolia USDC from Aave faucet'}
title={'Mint USDC'}
>
<MintUSDC onSuccess={refetchBalance} />
</Wrapper>
) : (
<BaseERC20ApproveAndTransferButton
amount={amount}
label={`Supply ${formattedAmount} USDC`}
labelSending="Sending..."
onSuccess={() => refetchBalance()}
spender={spender}
token={tokenUSDC_sepolia}
transaction={handleTransaction}
/>
)}
</WalletStatusVerifier>
)
})

export default ERC20ApproveAndTransferButton
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Wrapper from '@/src/components/pageComponents/home/Examples/demos/TransactionButton/Wrapper'
import TransactionButton from '@/src/components/sharedComponents/TransactionButton'
import { withWalletStatusVerifier } from '@/src/components/sharedComponents/WalletStatusVerifier'
import { WalletStatusVerifier } from '@/src/components/sharedComponents/WalletStatusVerifier'
import { GeneralMessage } from '@/src/components/sharedComponents/ui/GeneralMessage'
import PrimaryButton from '@/src/components/sharedComponents/ui/PrimaryButton'
import { useWeb3StatusConnected } from '@/src/hooks/useWeb3Status'
Expand All @@ -15,32 +15,32 @@ import { useSendTransaction } from 'wagmi'
*
* Works only on Sepolia chain.
*/
const NativeToken = withWalletStatusVerifier(
() => {
const [isModalOpen, setIsModalOpen] = useState(false)
const { address } = useWeb3StatusConnected()
const { sendTransactionAsync } = useSendTransaction()
const [minedMessage, setMinedMessage] = useState<string | ReactElement>()
const NativeToken = () => {
const [isModalOpen, setIsModalOpen] = useState(false)
const { address } = useWeb3StatusConnected()
const { sendTransactionAsync } = useSendTransaction()
const [minedMessage, setMinedMessage] = useState<string | ReactElement>()

const handleOnMined = (receipt: TransactionReceipt) => {
setMinedMessage(
<>
<b>Hash:</b> <span>{receipt.transactionHash}</span>
</>,
)
setIsModalOpen(true)
}
const handleOnMined = (receipt: TransactionReceipt) => {
setMinedMessage(
<>
<b>Hash:</b> <span>{receipt.transactionHash}</span>
</>,
)
setIsModalOpen(true)
}

const handleSendTransaction = (): Promise<Hash> => {
// Send native token
return sendTransactionAsync({
to: address,
value: parseEther('0.1'),
})
}
handleSendTransaction.methodId = 'sendTransaction'
const handleSendTransaction = (): Promise<Hash> => {
// Send native token
return sendTransactionAsync({
to: address,
value: parseEther('0.1'),
})
}
handleSendTransaction.methodId = 'sendTransaction'

return (
return (
<WalletStatusVerifier chainId={sepolia.id}>
<Dialog.Root
open={isModalOpen}
size="xs"
Expand Down Expand Up @@ -77,11 +77,8 @@ const NativeToken = withWalletStatusVerifier(
</Dialog.Content>
</Dialog.Positioner>
</Dialog.Root>
)
},
{
chainId: sepolia.id, // this DEMO component only works on sepolia chain
},
)
</WalletStatusVerifier>
)
}

export default NativeToken
135 changes: 135 additions & 0 deletions src/components/sharedComponents/SignButton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { ChakraProvider, createSystem, defaultConfig } from '@chakra-ui/react'
import { render, screen } from '@testing-library/react'
import type { ReactNode } from 'react'
import { createElement } from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'

const mockSwitchChain = vi.fn()
const mockSignMessageAsync = vi.fn()
const mockWatchSignature = vi.fn()

vi.mock('@/src/hooks/useWalletStatus', () => ({
useWalletStatus: vi.fn(() => ({
isReady: false,
needsConnect: true,
needsChainSwitch: false,
targetChain: { id: 1, name: 'Ethereum' },
targetChainId: 1,
switchChain: mockSwitchChain,
})),
}))

vi.mock('@/src/providers/Web3Provider', () => ({
ConnectWalletButton: () =>
createElement(
'button',
{ type: 'button', 'data-testid': 'connect-wallet-button' },
'Connect Wallet',
),
}))

vi.mock('@/src/providers/TransactionNotificationProvider', () => ({
useTransactionNotification: vi.fn(() => ({
watchSignature: mockWatchSignature,
})),
}))

vi.mock('wagmi', () => ({
useSignMessage: vi.fn(() => ({
isPending: false,
signMessageAsync: mockSignMessageAsync,
})),
}))

const { useWalletStatus } = await import('@/src/hooks/useWalletStatus')
const mockedUseWalletStatus = vi.mocked(useWalletStatus)

const system = createSystem(defaultConfig)

const renderWithChakra = (ui: ReactNode) =>
render(<ChakraProvider value={system}>{ui}</ChakraProvider>)

describe('SignButton', () => {
beforeEach(() => {
vi.clearAllMocks()
})

it('renders connect button when wallet needs connect', async () => {
mockedUseWalletStatus.mockReturnValue({
isReady: false,
needsConnect: true,
needsChainSwitch: false,
targetChain: { id: 1, name: 'Ethereum' } as ReturnType<typeof useWalletStatus>['targetChain'],
targetChainId: 1,
switchChain: mockSwitchChain,
})

const { default: SignButton } = await import('./SignButton')

renderWithChakra(<SignButton message="Hello" />)

expect(screen.getByTestId('connect-wallet-button')).toBeInTheDocument()
expect(screen.queryByText('Sign Message')).toBeNull()
})

it('renders custom fallback when provided and wallet needs connect', async () => {
mockedUseWalletStatus.mockReturnValue({
isReady: false,
needsConnect: true,
needsChainSwitch: false,
targetChain: { id: 1, name: 'Ethereum' } as ReturnType<typeof useWalletStatus>['targetChain'],
targetChainId: 1,
switchChain: mockSwitchChain,
})

const { default: SignButton } = await import('./SignButton')

renderWithChakra(
<SignButton
message="Hello"
fallback={createElement('div', { 'data-testid': 'custom-fallback' }, 'Custom')}
/>,
)

expect(screen.getByTestId('custom-fallback')).toBeInTheDocument()
expect(screen.queryByText('Sign Message')).toBeNull()
})

it('renders switch chain button when wallet needs chain switch', async () => {
mockedUseWalletStatus.mockReturnValue({
isReady: false,
needsConnect: false,
needsChainSwitch: true,
targetChain: { id: 10, name: 'OP Mainnet' } as ReturnType<
typeof useWalletStatus
>['targetChain'],
targetChainId: 10,
switchChain: mockSwitchChain,
})

const { default: SignButton } = await import('./SignButton')

renderWithChakra(<SignButton message="Hello" />)

expect(screen.getByText(/Switch to/)).toBeInTheDocument()
expect(screen.getByText(/OP Mainnet/)).toBeInTheDocument()
expect(screen.queryByText('Sign Message')).toBeNull()
})

it('renders sign button when wallet is ready', async () => {
mockedUseWalletStatus.mockReturnValue({
isReady: true,
needsConnect: false,
needsChainSwitch: false,
targetChain: { id: 1, name: 'Ethereum' } as ReturnType<typeof useWalletStatus>['targetChain'],
targetChainId: 1,
switchChain: mockSwitchChain,
})

const { default: SignButton } = await import('./SignButton')

renderWithChakra(<SignButton message="Hello" />)

expect(screen.getByText('Sign Message')).toBeInTheDocument()
})
})
Loading
Loading