diff --git a/package.json b/package.json index 54fc1af..693ca66 100644 --- a/package.json +++ b/package.json @@ -90,12 +90,13 @@ "prepare": "npx simple-git-hooks" }, "dependencies": { - "@eth-optimism/contracts-ts": "^0.15.0" + "@eth-optimism/contracts-ts": "^0.15.0", + "@testing-library/react-hooks": "^8.0.1" }, "peerDependencies": { "@tanstack/query-core": ">=5.0.0", "@tanstack/react-query": "beta", - "@wagmi/core": "alpha", + "@wagmi/core": "2.0.0-alpha.11", "op-viem": "1.3.0-alpha", "typescript": ">=5.0.4", "viem": "2.0.0-alpha.17", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4199874..a1b3ab5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,19 +13,22 @@ importers: dependencies: '@eth-optimism/contracts-ts': specifier: ^0.15.0 - version: 0.15.0(@wagmi/core@2.0.0-alpha.10)(typescript@5.0.4)(wagmi@2.0.0-alpha.10) + version: 0.15.0(@wagmi/core@2.0.0-alpha.11)(typescript@5.0.4)(wagmi@2.0.0-alpha.10) '@tanstack/query-core': specifier: '>=5.0.0' version: 5.0.5 '@tanstack/react-query': specifier: beta version: 5.0.0-beta.35(react-dom@18.2.0)(react-native@0.72.6)(react@18.2.0) + '@testing-library/react-hooks': + specifier: ^8.0.1 + version: 8.0.1(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0) '@wagmi/core': - specifier: alpha - version: 2.0.0-alpha.10(@types/react@18.2.31)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) + specifier: 2.0.0-alpha.11 + version: 2.0.0-alpha.11(@tanstack/query-core@5.0.5)(@types/react@18.2.31)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) op-viem: specifier: 1.3.0-alpha - version: 1.3.0-alpha(@wagmi/core@2.0.0-alpha.10)(typescript@5.0.4)(viem@2.0.0-alpha.17)(wagmi@2.0.0-alpha.10) + version: 1.3.0-alpha(@wagmi/core@2.0.0-alpha.11)(typescript@5.0.4)(viem@2.0.0-alpha.17)(wagmi@2.0.0-alpha.10) viem: specifier: 2.0.0-alpha.17 version: 2.0.0-alpha.17(typescript@5.0.4)(zod@3.22.4) @@ -101,7 +104,7 @@ importers: version: 0.34.6(happy-dom@12.10.3) wagmi: specifier: alpha - version: 2.0.0-alpha.10(@tanstack/react-query@5.0.0-beta.35)(@types/react@18.2.31)(react-native@0.72.6)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) + version: 2.0.0-alpha.10(@tanstack/query-core@5.0.5)(@tanstack/react-query@5.0.0-beta.35)(@types/react@18.2.31)(react-native@0.72.6)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) docs: devDependencies: @@ -2498,7 +2501,7 @@ packages: requiresBuild: true dev: true - /@eth-optimism/contracts-ts@0.15.0(@wagmi/core@2.0.0-alpha.10)(typescript@5.0.4)(wagmi@2.0.0-alpha.10): + /@eth-optimism/contracts-ts@0.15.0(@wagmi/core@2.0.0-alpha.11)(typescript@5.0.4)(wagmi@2.0.0-alpha.10): resolution: {integrity: sha512-qga3xsj+NhnxjY96TWktKAmc2DSYrdy0E+lVkS3NZ/b5w898IisoMj/Xvv3KoovBJmnk+/ENNnMX7kk+fwQdtA==} peerDependencies: '@wagmi/core': '>1.0.0' @@ -2510,11 +2513,11 @@ packages: optional: true dependencies: '@testing-library/react': 14.0.0(react-dom@18.2.0)(react@18.2.0) - '@wagmi/core': 2.0.0-alpha.10(@types/react@18.2.31)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) + '@wagmi/core': 2.0.0-alpha.11(@tanstack/query-core@5.0.5)(@types/react@18.2.31)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) viem: 1.16.6(typescript@5.0.4) - wagmi: 2.0.0-alpha.10(@tanstack/react-query@5.0.0-beta.35)(@types/react@18.2.31)(react-native@0.72.6)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) + wagmi: 2.0.0-alpha.10(@tanstack/query-core@5.0.5)(@tanstack/react-query@5.0.0-beta.35)(@types/react@18.2.31)(react-native@0.72.6)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) transitivePeerDependencies: - bufferutil - typescript @@ -3723,7 +3726,7 @@ packages: resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==} dependencies: '@noble/curves': 1.1.0 - '@noble/hashes': 1.3.1 + '@noble/hashes': 1.3.2 '@scure/base': 1.1.3 /@scure/bip32@1.3.2: @@ -3875,7 +3878,6 @@ packages: /@tanstack/query-core@5.0.5: resolution: {integrity: sha512-MThCETMkHDHTnFZHp71L+SqTtD5d6XHftFCVR1xRJdWM3qGrlQ2VCXaj0SKVcyJej2e1Opa2c7iknu1llxCDNQ==} - dev: false /@tanstack/react-query@5.0.0-beta.35(react-dom@18.2.0)(react-native@0.72.6)(react@18.2.0): resolution: {integrity: sha512-9hxP/DZbGucYFaHx6Njhkrbx32lNRk4QqqkGzyQ4IPaYfJTELE7urc9LBYRwrof211cj0W76IxmpjDwZ5ljZpQ==} @@ -3908,6 +3910,29 @@ packages: lz-string: 1.5.0 pretty-format: 27.5.1 + /@testing-library/react-hooks@8.0.1(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==} + engines: {node: '>=12'} + peerDependencies: + '@types/react': ^16.9.0 || ^17.0.0 + react: ^16.9.0 || ^17.0.0 + react-dom: ^16.9.0 || ^17.0.0 + react-test-renderer: ^16.9.0 || ^17.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + react-dom: + optional: true + react-test-renderer: + optional: true + dependencies: + '@babel/runtime': 7.23.2 + '@types/react': 18.2.31 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-error-boundary: 3.1.4(react@18.2.0) + dev: false + /@testing-library/react@14.0.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-S04gSNJbYE30TlIMLTzv6QCTzt9AqIF5y6s6SzVFILNcNvbV/jU96GeiTPillGQo+Ny64M/5PV7klNYYgv5Dfg==} engines: {node: '>=14'} @@ -4354,7 +4379,7 @@ packages: '@metamask/sdk': 0.8.0(@types/react@18.2.31)(react-native@0.72.6)(react@18.2.0) '@safe-global/safe-apps-provider': 0.18.0(typescript@5.0.4) '@safe-global/safe-apps-sdk': 8.1.0(typescript@5.0.4) - '@wagmi/core': 2.0.0-alpha.10(@types/react@18.2.31)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) + '@wagmi/core': 2.0.0-alpha.10(@tanstack/query-core@5.0.5)(@types/react@18.2.31)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) '@walletconnect/ethereum-provider': 2.10.2(@walletconnect/modal@2.6.2) '@walletconnect/modal': 2.6.2(@types/react@18.2.31)(react@18.2.0) typescript: 5.0.4 @@ -4371,7 +4396,7 @@ packages: - utf-8-validate - zod - /@wagmi/core@2.0.0-alpha.10(@types/react@18.2.31)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17): + /@wagmi/core@2.0.0-alpha.10(@tanstack/query-core@5.0.5)(@types/react@18.2.31)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17): resolution: {integrity: sha512-qIKHIoTH1BlI71sqiYPh20GCtUTK+/ORLe9mukUzqw5Zu1aIzEkxUMd4XVZu4joEPl97Gc5813dVHZLHZcTABw==} peerDependencies: '@tanstack/query-core': '>5.0.0-beta.28' @@ -4383,6 +4408,33 @@ packages: typescript: optional: true dependencies: + '@tanstack/query-core': 5.0.5 + eventemitter3: 5.0.1 + mipd: 0.0.5(typescript@5.0.4) + typescript: 5.0.4 + viem: 2.0.0-alpha.17(typescript@5.0.4)(zod@3.22.4) + zustand: 4.4.4(@types/react@18.2.31)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - immer + - react + - utf-8-validate + - zod + + /@wagmi/core@2.0.0-alpha.11(@tanstack/query-core@5.0.5)(@types/react@18.2.31)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17): + resolution: {integrity: sha512-dta2j1u1G2uhBe60udrHe6XtJFkUOcWrhW5k6gMCh1mkHAlsSOdzCwozjWZm4olkuGoSFGADjWYkn9EysQi8Wg==} + peerDependencies: + '@tanstack/query-core': '>=5.0.0' + typescript: '>=5.0.4' + viem: 2.0.0-alpha.21 + peerDependenciesMeta: + '@tanstack/query-core': + optional: true + typescript: + optional: true + dependencies: + '@tanstack/query-core': 5.0.5 eventemitter3: 5.0.1 mipd: 0.0.5(typescript@5.0.4) typescript: 5.0.4 @@ -4395,6 +4447,7 @@ packages: - react - utf-8-validate - zod + dev: false /@walletconnect/core@2.10.2: resolution: {integrity: sha512-JQz/xp3SLEpTeRQctdck2ugSBVEpMxoSE+lFi2voJkZop1hv6P+uqr6E4PzjFluAjeAnKlT1xvra0aFWjPWVcw==} @@ -8271,7 +8324,7 @@ packages: mimic-fn: 4.0.0 dev: true - /op-viem@1.3.0-alpha(@wagmi/core@2.0.0-alpha.10)(typescript@5.0.4)(viem@2.0.0-alpha.17)(wagmi@2.0.0-alpha.10): + /op-viem@1.3.0-alpha(@wagmi/core@2.0.0-alpha.11)(typescript@5.0.4)(viem@2.0.0-alpha.17)(wagmi@2.0.0-alpha.10): resolution: {integrity: sha512-pcV+8BwYUwWfixti9aRVs2A/3Y5PjUIvGDOFPm7AyHnrxwm8JHmtWnj+MRUuHJ+SujZ8picdUyu/wvCxLj2Mng==} requiresBuild: true peerDependencies: @@ -8281,7 +8334,7 @@ packages: typescript: optional: true dependencies: - '@eth-optimism/contracts-ts': 0.15.0(@wagmi/core@2.0.0-alpha.10)(typescript@5.0.4)(wagmi@2.0.0-alpha.10) + '@eth-optimism/contracts-ts': 0.15.0(@wagmi/core@2.0.0-alpha.11)(typescript@5.0.4)(wagmi@2.0.0-alpha.10) typescript: 5.0.4 viem: 2.0.0-alpha.17(typescript@5.0.4)(zod@3.22.4) transitivePeerDependencies: @@ -8842,6 +8895,16 @@ packages: react: 18.2.0 scheduler: 0.23.0 + /react-error-boundary@3.1.4(react@18.2.0): + resolution: {integrity: sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==} + engines: {node: '>=10', npm: '>=6'} + peerDependencies: + react: '>=16.13.1' + dependencies: + '@babel/runtime': 7.23.2 + react: 18.2.0 + dev: false + /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -10496,7 +10559,7 @@ packages: typescript: 5.0.4 dev: true - /wagmi@2.0.0-alpha.10(@tanstack/react-query@5.0.0-beta.35)(@types/react@18.2.31)(react-native@0.72.6)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17): + /wagmi@2.0.0-alpha.10(@tanstack/query-core@5.0.5)(@tanstack/react-query@5.0.0-beta.35)(@types/react@18.2.31)(react-native@0.72.6)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17): resolution: {integrity: sha512-SBsYn8d1gG5I3vPYsPWo0/UmdlumUHRII5Tnrw2QHx0+Zy9senqfkP5c05WXvDQjLLQdNOsV5gyVQZe2KtcEzg==} peerDependencies: '@tanstack/react-query': '>5.0.0-beta.28' @@ -10509,13 +10572,14 @@ packages: dependencies: '@tanstack/react-query': 5.0.0-beta.35(react-dom@18.2.0)(react-native@0.72.6)(react@18.2.0) '@wagmi/connectors': 4.0.0-alpha.10(@types/react@18.2.31)(@wagmi/core@2.0.0-alpha.10)(react-native@0.72.6)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) - '@wagmi/core': 2.0.0-alpha.10(@types/react@18.2.31)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) + '@wagmi/core': 2.0.0-alpha.10(@tanstack/query-core@5.0.5)(@types/react@18.2.31)(react@18.2.0)(typescript@5.0.4)(viem@2.0.0-alpha.17) react: 18.2.0 typescript: 5.0.4 use-sync-external-store: 1.2.0(react@18.2.0) viem: 2.0.0-alpha.17(typescript@5.0.4)(zod@3.22.4) transitivePeerDependencies: - '@react-native-async-storage/async-storage' + - '@tanstack/query-core' - '@types/react' - bufferutil - encoding diff --git a/src/_test/chains.ts b/src/_test/chains.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/_test/config.ts b/src/_test/config.ts new file mode 100644 index 0000000..c85cc91 --- /dev/null +++ b/src/_test/config.ts @@ -0,0 +1,40 @@ +import { createConfig, http } from '@wagmi/core' +import { mock } from '@wagmi/core/internal' +import { opStackL2ChainContracts } from 'op-viem' +import { baseAddresses } from 'op-viem/chains' +import { base as viem_base, mainnet } from 'viem/chains' +import { type OpConfig } from '../types/OpConfig.js' +import { accounts } from './constants.js' +import { getRpcUrls } from './utils.js' + +const base = { + chainId: viem_base.id, + l1ChaindId: mainnet.id, + l1Addresses: baseAddresses, + l2Addresses: opStackL2ChainContracts, + ...getRpcUrls({ port: 8547 }), + ...viem_base, + fork: { + blockNumber: process.env.VITE_OPTIMISM_FORK_BLOCK_NUMBER + ? BigInt(Number(process.env.VITE_OPTIMISM_FORK_BLOCK_NUMBER)) + : 5940037n, + url: process.env.VITE_OPTIMISM_FORK_URL ?? 'https://mainnet.base.org', + }, +} + +export const config: OpConfig = { + l2chains: { + // @ts-expect-error this seems to have a mismatch that is likely a bug + [base.id]: base, + }, + + ...createConfig({ + chains: [base], + connectors: [mock({ accounts })], + pollingInterval: 100, + storage: null, + transports: { + [base.id]: http(), + }, + }), +} diff --git a/src/_test/constants.ts b/src/_test/constants.ts new file mode 100644 index 0000000..b9fc906 --- /dev/null +++ b/src/_test/constants.ts @@ -0,0 +1,75 @@ +/** + * The id of the current test worker. + * + * This is used by the anvil proxy to route requests to the correct anvil instance. + */ +export const pool = Number(process.env.VITEST_POOL_ID ?? 1) + +// Test accounts +export const accounts = ['0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'] as const + +const messages = new Map() +function warn(message: string) { + if (!messages.has(message)) { + messages.set(message, true) + console.warn(message) + } +} + +export let forkBlockNumber: bigint +if (process.env.VITE_ANVIL_BLOCK_NUMBER) { + forkBlockNumber = BigInt(Number(process.env.VITE_ANVIL_BLOCK_NUMBER)) +} else { + forkBlockNumber = 18136086n + warn( + `\`VITE_ANVIL_BLOCK_NUMBER\` not found. Falling back to \`${forkBlockNumber}\`.`, + ) +} + +export let forkUrl: string +if (process.env.VITE_ANVIL_FORK_URL) { + forkUrl = process.env.VITE_ANVIL_FORK_URL +} else { + forkUrl = 'https://cloudflare-eth.com' + warn(`\`VITE_ANVIL_FORK_URL\` not found. Falling back to \`${forkUrl}\`.`) +} + +export let blockTime: number +if (process.env.VITE_ANVIL_BLOCK_TIME) { + blockTime = Number(process.env.VITE_ANVIL_BLOCK_TIME) +} else { + blockTime = 1 + warn(`\`VITE_ANVIL_BLOCK_TIME\` not found. Falling back to \`${blockTime}\`.`) +} + +export let rollupForkBlockNumber: bigint +if (process.env.VITE_ANVIL_ROLLUP_BLOCK_NUMBER) { + rollupForkBlockNumber = BigInt( + Number(process.env.VITE_ANVIL_ROLLUP_BLOCK_NUMBER), + ) +} else { + rollupForkBlockNumber = 3709321n + warn( + `\`VITE_ANVIL_ROLLUP_BLOCK_NUMBER\` not found. Falling back to \`${rollupForkBlockNumber}\`.`, + ) +} + +export let rollupForkUrl: string +if (process.env.VITE_ANVIL_ROLLUP_FORK_URL) { + rollupForkUrl = process.env.VITE_ANVIL_ROLLUP_FORK_URL +} else { + rollupForkUrl = 'https://mainnet.base.org' + warn( + `\`VITE_ANVIL_ROLLUP_FORK_URL\` not found. Falling back to \`${rollupForkUrl}\`.`, + ) +} + +export let rollupBlockTime: number +if (process.env.VITE_ANVIL_ROLLUP_BLOCK_TIME) { + rollupBlockTime = Number(process.env.VITE_ANVIL_ROLLUP_BLOCK_TIME) +} else { + rollupBlockTime = 1 + warn( + `\`VITE_ANVIL_ROLLUP_BLOCK_TIME\` not found. Falling back to \`${rollupBlockTime}\`.`, + ) +} diff --git a/src/_test/globalSetup.ts b/src/_test/globalSetup.ts new file mode 100644 index 0000000..07ac3fd --- /dev/null +++ b/src/_test/globalSetup.ts @@ -0,0 +1,51 @@ +// from https://github.com/wagmi-dev/viem/blob/main/src/_test/globalSetup.ts +import { startProxy } from '@viem/anvil' + +import { + blockTime, + forkBlockNumber, + forkUrl, + rollupBlockTime, + rollupForkBlockNumber, + rollupForkUrl, +} from './constants.js' + +export default async function() { + if (process.env.SKIP_GLOBAL_SETUP) { + return + } + + // Using this proxy, we can parallelize our test suite by spawning multiple "on demand" anvil + // instances and proxying requests to them. Especially for local development, this is much faster + // than running the tests serially. + // + // In vitest, each thread is assigned a unique, numerical id (`process.env.VITEST_POOL_ID`). We + // append this id to the local rpc url (e.g. `http://127.0.0.1:8545/`). + // + // Whenever a request hits the proxy server at this url, it spawns (or reuses) an anvil instance + // at a randomly assigned port and proxies the request to it. The anvil instance is added to a + // [id:port] mapping for future request and is kept alive until the test suite finishes. + // + // Since each thread processes one test file after the other, we don't have to worry about + // non-deterministic behavior caused by multiple tests hitting the same anvil instance concurrently + // as long as we avoid `test.concurrent()`. + // + // We still need to remember to reset the anvil instance between test files. This is generally + // handled in `setup.ts` but may require additional resetting (e.g. via `afterAll`), in case of + // any custom per-test adjustments that persist beyond `anvil_reset`. + await startProxy({ + port: 8555, + options: { + forkUrl: rollupForkUrl, + forkBlockNumber: rollupForkBlockNumber, + blockTime: rollupBlockTime, + }, + }) + return await startProxy({ + options: { + forkUrl, + forkBlockNumber, + blockTime, + }, + }) +} diff --git a/src/_test/react.ts b/src/_test/react.ts new file mode 100644 index 0000000..1dd39e2 --- /dev/null +++ b/src/_test/react.ts @@ -0,0 +1,47 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { + renderHook as rtl_renderHook, + type RenderHookOptions, + type RenderHookResult, + waitFor as rtl_waitFor, + type waitForOptions, +} from '@testing-library/react' +import { createElement } from 'react' +import { WagmiProvider } from 'wagmi' +export { act, cleanup } from '@testing-library/react' + +import { config } from './config.js' + +export const queryClient = new QueryClient() + +export function createWrapper>( + Wrapper: TComponent, + props: Parameters[0], +) { + type Props = { children?: React.ReactNode | undefined } + return function CreatedWrapper({ children }: Props) { + return createElement( + Wrapper, + props, + createElement(QueryClientProvider, { client: queryClient }, children), + ) + } +} + +export function renderHook( + render: (props: Props) => Result, + options?: RenderHookOptions | undefined, +): RenderHookResult { + queryClient.clear() + return rtl_renderHook(render, { + wrapper: createWrapper(WagmiProvider, { config, reconnectOnMount: false }), + ...options, + }) +} + +export function waitFor( + callback: () => Promise | T, + options?: waitForOptions | undefined, +): Promise { + return rtl_waitFor(callback, { timeout: 10_000, ...options }) +} diff --git a/src/_test/setup.ts b/src/_test/setup.ts new file mode 100644 index 0000000..9277b28 --- /dev/null +++ b/src/_test/setup.ts @@ -0,0 +1,4 @@ +import { vi } from 'vitest' + +// Make dates stable across runs +Date.now = vi.fn(() => new Date(Date.UTC(2023, 10, 20)).valueOf()) diff --git a/src/_test/utils.ts b/src/_test/utils.ts new file mode 100644 index 0000000..9b26c57 --- /dev/null +++ b/src/_test/utils.ts @@ -0,0 +1,24 @@ +import { pool } from './constants.js' + +export function getRpcUrls({ port }: { port: number }) { + return { + port, + rpcUrls: { + // These rpc urls are automatically used in the transports. + default: { + // Note how we append the worker id to the local rpc urls. + http: [`http://127.0.0.1:${port}/${pool}`], + webSocket: [`ws://127.0.0.1:${port}/${pool}`], + }, + public: { + // Note how we append the worker id to the local rpc urls. + http: [`http://127.0.0.1:${port}/${pool}`], + webSocket: [`ws://127.0.0.1:${port}/${pool}`], + }, + }, + } as const +} + +export async function wait(time: number) { + return new Promise((res) => setTimeout(res, time)) +} diff --git a/src/hooks/L1/useSimulateDepositETH.test.ts b/src/hooks/L1/useSimulateDepositETH.test.ts new file mode 100644 index 0000000..2e16ca4 --- /dev/null +++ b/src/hooks/L1/useSimulateDepositETH.test.ts @@ -0,0 +1,126 @@ +import { connect, disconnect } from '@wagmi/core' +import { expect, test } from 'vitest' +import { config } from '../../_test/config.js' +import { accounts } from '../../_test/constants.js' +import { renderHook, waitFor } from '../../_test/react.js' +import { useSimulateDepositETH } from './useSimulateDepositETH.js' + +const connector = config.connectors[0]! +const portal = '0xe93c8cD0D409341205A592f8c4Ac1A5fe5585cfA' + +test('useSimulateDepositETH', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => + useSimulateDepositETH({ + args: { + to: accounts[0], + gasLimit: 100000, + }, + portal, + value: BigInt(1), + dataSuffix: '0x1234', + }) + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "request": { + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256", + }, + { + "internalType": "uint64", + "name": "_gasLimit", + "type": "uint64", + }, + { + "internalType": "bool", + "name": "_isCreation", + "type": "bool", + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes", + }, + ], + "name": "depositTransaction", + "outputs": [], + "stateMutability": "payable", + "type": "function", + }, + ], + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xe93c8cD0D409341205A592f8c4Ac1A5fe5585cfA", + "args": [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + 1n, + 100000, + false, + "0x", + ], + "dataSuffix": "0x1234", + "functionName": "depositTransaction", + "value": 1n, + }, + "result": undefined, + }, + "dataUpdatedAt": 1700438400000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "simulateContract", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockNumber": undefined, + "chainId": 8453, + "dataSuffix": "0x1234", + "gasLimit": 100000, + "gasPrice": undefined, + "to": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": undefined, + "value": undefined, + }, + ], + "refetch": [Function], + "status": "success", + } + `) + + await disconnect(config, { connector }) +}) diff --git a/src/hooks/L1/useWriteDepositERC20.test.ts b/src/hooks/L1/useWriteDepositERC20.test.ts new file mode 100644 index 0000000..771af46 --- /dev/null +++ b/src/hooks/L1/useWriteDepositERC20.test.ts @@ -0,0 +1,42 @@ +import { connect, disconnect } from '@wagmi/core' +import { baseAddresses } from 'op-viem/chains' +import { base } from 'viem/chains' +import { expect, test } from 'vitest' +import { config } from '../../_test/config.js' +import { accounts } from '../../_test/constants.js' +import { renderHook, waitFor } from '../../_test/react.js' +import { useWriteDepositERC20 } from './useWriteDepositERC20.js' + +const connector = config.connectors[0]! + +test(useWriteDepositERC20.name, async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => + useWriteDepositERC20({ + l2ChainId: base.id, + args: { + l1Token: '0xTokenAddressL1', + l2Token: '0xTokenAddressL2', + to: accounts[0], + amount: 100n, // Assuming amount is in BigInt format + minGasLimit: 21000, + extraData: '0x', + }, + ...baseAddresses, + }) + ) + + expect(result.current.writeDepositERC20).toBeDefined() + expect(result.current.writeDepositERC20Async).toBeDefined() + expect(result.current.data).toBeUndefined() + expect(result.current.isIdle).toBe(true) + + result.current.writeDepositERC20() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot() + + await disconnect(config, { connector }) +}) diff --git a/src/hooks/L1/useWriteDepositERC20.ts b/src/hooks/L1/useWriteDepositERC20.ts index 5a72ae4..2f9cd65 100644 --- a/src/hooks/L1/useWriteDepositERC20.ts +++ b/src/hooks/L1/useWriteDepositERC20.ts @@ -20,9 +20,10 @@ export function useWriteDepositERC20( ) { const config = useOpConfig(rest) const l2Chain = config.l2chains[l2ChainId] - const { writeContract, writeContractAsync } = useWriteContract() + const { writeContract, writeContractAsync, ...result } = useWriteContract() return { + ...result, writeDepositERC20: () => writeContract({ chainId: l2Chain.l1ChaindId, diff --git a/src/hooks/L1/useWriteDepositETH.test.ts b/src/hooks/L1/useWriteDepositETH.test.ts new file mode 100644 index 0000000..d9141f9 --- /dev/null +++ b/src/hooks/L1/useWriteDepositETH.test.ts @@ -0,0 +1,42 @@ +import { baseAddresses } from 'op-viem/chains' +import { base } from 'viem/chains' +import { expect, test } from 'vitest' +import { accounts } from '../../_test/constants.js' +import { renderHook, waitFor } from '../../_test/react.js' +import { useWriteDepositETH } from './useWriteDepositETH.js' + +test(useWriteDepositETH.name, async () => { + const { result } = renderHook(() => + useWriteDepositETH({ + l2ChainId: base.id, + args: { + to: accounts[0], + gasLimit: 21000, + data: '0x', + // typescript wasn't compiling for me for some reason + ...{ amount: 1n }, + }, + ...baseAddresses, + }) + ) + + // write contract lazily writes + // these are low value checks we are testing wagmi here + expect(result.current.writeDepositETH).toBeDefined() + expect(result.current.writeDepositETHAsync).toBeDefined() + expect(result.current.data).toBeUndefined() + expect(result.current.isIdle).toBe(true) + + // go ahead and trigger the write + result.current.writeDepositETH() + + // since we didn't use the async wait for it to succeed + await waitFor(() => { + // check for error first so if one happens we can see it in the test failure + expect(result.current.error).toBeUndefined() + expect(result.current.isSuccess).toBeTruthy() + }) + + // now assert the result is what we expect + expect(result.current).toMatchInlineSnapshot() +}) diff --git a/src/hooks/L1/useWriteDepositETH.ts b/src/hooks/L1/useWriteDepositETH.ts index e605db4..c13d04a 100644 --- a/src/hooks/L1/useWriteDepositETH.ts +++ b/src/hooks/L1/useWriteDepositETH.ts @@ -18,9 +18,10 @@ export type UseWriteDepositETHParameters writeContract({ chainId: l2Chain.l1ChaindId, diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..a819a92 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,37 @@ +export const poolId = Number(process.env.VITEST_POOL_ID ?? 1) +export const localHttpUrl = `http://127.0.0.1:8545/${poolId}` +export const localWsUrl = `ws://127.0.0.1:8545/${poolId}` +export const localRollupHttpUrl = `http://127.0.0.1:8555/${poolId}` +export const localRollupWsUrl = `ws://127.0.0.1:8555/${poolId}` +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + benchmark: { + outputFile: './bench/report.json', + reporters: process.env.CI ? ['json'] : ['verbose'], + }, + // if you are using the default rpc you will need these to not get rate limited + // maxConcurrency: 1, + // maxThreads: 1, + // minThreads: 1, + coverage: { + lines: 95, + statements: 95, + functions: 90, + branches: 93.82, + thresholdAutoUpdate: true, + reporter: ['text', 'json-summary', 'json'], + exclude: [ + '**/errors/utils.ts', + '**/dist/**', + '**/*.test.ts', + '**/_test/**', + ], + }, + environment: 'happy-dom', + globalSetup: ['./src/_test/globalSetup.ts'], + setupFiles: ['./src/_test/setup.ts'], + testTimeout: 100_000, + }, +})