diff --git a/.agents/README.md b/.agents/README.md new file mode 100644 index 0000000000..bb82148739 --- /dev/null +++ b/.agents/README.md @@ -0,0 +1 @@ +@_@ diff --git a/.agents/skills/wagmi-development/SKILL.md b/.agents/skills/wagmi-development/SKILL.md new file mode 100644 index 0000000000..f5a1dc6d8d --- /dev/null +++ b/.agents/skills/wagmi-development/SKILL.md @@ -0,0 +1,520 @@ +--- +name: wagmi-development +description: Creates Wagmi features across all layers - core actions, query options, framework bindings. Use when adding new actions, hooks, or working across packages/core, packages/react, packages/vue. +--- + +# Wagmi Development + +Full-stack patterns for adding Wagmi features. This skill covers Viem-based actions only (not Wagmi config actions). + +## Layer Overview + +1. **Core Action** (`packages/core/src/actions/`) - Base functionality wrapping Viem +2. **Query Options** (`packages/core/src/query/`) - TanStack Query integration +3. **Framework Bindings** - React (`packages/react/src/hooks/`), Vue (`packages/vue/src/composables/`) + +--- + +## 1. Core Action + +### Structure + +```ts +import { + type MyActionErrorType as viem_MyActionErrorType, + type MyActionParameters as viem_MyActionParameters, + type MyActionReturnType as viem_MyActionReturnType, + myAction as viem_myAction, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter, ConnectorParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type MyActionParameters = Compute< + ChainIdParameter & viem_MyActionParameters +> + +export type MyActionReturnType = viem_MyActionReturnType + +export type MyActionErrorType = viem_MyActionErrorType + +/** https://wagmi.sh/core/api/actions/myAction */ +export async function myAction( + config: config, + parameters: MyActionParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_myAction, 'myAction') + return action(rest) +} +``` + +### Key Rules + +- **Viem imports**: Prefix with `viem_` (e.g., `viem_getBalance`) +- **Client access**: + - Read-only: `config.getClient({ chainId })` + - Wallet: `await getConnectorClient(config, { chainId, connector, account })` + - Mixed: Use `getConnectorClient` for account, `getClient` for action (see `estimateGas.ts`) +- **Parameters**: Add `ChainIdParameter` always. Add `ConnectorParameter` for wallet actions. +- **Type params**: Mirror Viem's type params for inference. Use `const` modifier for literal inference (abi, args). +- **Spread**: Omit wagmi-specific props (`chainId`, `connector`) when calling Viem action. + +### Testing + +**Runtime tests** (`action.test.ts`): +```ts +import { abi, address, config } from '@wagmi/test' +import { expect, test } from 'vitest' +import { myAction } from './myAction.js' + +test('default', async () => { + await expect(myAction(config, { /* required params */ })).resolves.toMatchInlineSnapshot(`...`) +}) + +test('parameters: chainId', async () => { /* test chainId param */ }) + +test('behavior: error case', async () => { /* test error handling */ }) +``` + +**Type tests** (`action.test-d.ts`) - only if action has type inference: +```ts +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' +import { myAction } from './myAction.js' + +test('default', async () => { + const result = await myAction(config, { /* params */ }) + expectTypeOf(result).toEqualTypeOf() +}) +``` + +**Type benchmarks** (`action.bench-d.ts`) - only if action has type inference: +```ts +import { attest } from '@ark/attest' +import { test } from 'vitest' +import type { MyActionParameters } from './myAction.js' + +test('default', () => { + type Result = MyActionParameters + const res = {} as Result + attest.instantiations([12345, 'instantiations']) + attest(res.args).type.toString.snap(`readonly [account: \`0x\${string}\`]`) +}) +``` + +**Wallet action tests**: Connect before, disconnect after: +```ts +test('default', async () => { + await connect(config, { connector }) + await expect(myAction(config, { /* params */ })).resolves.toMatchInlineSnapshot(`...`) + await disconnect(config, { connector }) +}) +``` + +--- + +## 2. Query Options + +Query (read-only) or Mutation (wallet) options for TanStack Query. + +### Query Structure + +```ts +import { + type MyActionErrorType, + type MyActionParameters, + type MyActionReturnType, + myAction, +} from '../actions/myAction.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { QueryOptions, QueryParameter } from '../types/query.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, structuralSharing } from './utils.js' + +export type MyActionOptions< + config extends Config, + selectData = MyActionData, +> = Compute> & ScopeKeyParameter> & + QueryParameter> + +export function myActionQueryOptions< + config extends Config, + selectData = MyActionData, +>( + export const fooAbi = [] as const + options: MyActionOptions = {}, +): MyActionQueryOptions { + return { + ...options.query, + enabled: Boolean(options.requiredParam && (options.query?.enabled ?? true)), + queryFn: async (context) => { + const [, { scopeKey: _, ...parameters }] = context.queryKey + if (!parameters.requiredParam) throw new Error('requiredParam is required') + const result = await myAction(config, { + ...(parameters as MyActionParameters), + requiredParam: parameters.requiredParam, + }) + return result ?? null + }, + queryKey: myActionQueryKey(options), + structuralSharing, // include when returning complex objects/arrays + } +} + +export type MyActionQueryFnData = Compute +export type MyActionData = MyActionQueryFnData + +export function myActionQueryKey( + options: Compute> & ScopeKeyParameter> = {}, +) { + return ['myAction', filterQueryOptions(options)] as const +} + +export type MyActionQueryKey = ReturnType> + +export type MyActionQueryOptions< + config extends Config, + selectData = MyActionData, +> = QueryOptions> +``` + +### Mutation Structure + +```ts +import type { MutationOptions, MutationParameter } from '../types/query.js' + +export type MyActionOptions = MutationParameter< + MyActionData, + MyActionErrorType, + MyActionVariables, + context +> + +export function myActionMutationOptions( + config: config, + options: MyActionOptions = {}, +): MyActionMutationOptions { + return { + ...options.mutation, + mutationFn: async (variables) => { + return myAction(config, variables) + }, + mutationKey: ['myAction'], + } +} + +export type MyActionMutationOptions = MutationOptions< + MyActionData, + MyActionErrorType, + MyActionVariables +> +``` + +### Key Rules + +- **ExactPartial vs UnionExactPartial**: Use `ExactPartial` for simple types, `UnionExactPartial` for complex unions (contract actions) +- **enabled**: Based on required params being truthy +- **structuralSharing**: Include when action returns objects/arrays +- **filterQueryOptions**: Filters common non-serializable props. Skip props like `onReplaced` manually in query key. +- **Query key**: Always `['actionName', filterQueryOptions(options)]` + +### Testing + +```ts +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' +import { myActionQueryOptions } from './myAction.js' + +test('default', () => { + expect(myActionQueryOptions(config, {})).toMatchInlineSnapshot(` + { + "enabled": false, + "queryFn": [Function], + "queryKey": ["myAction", {}], + } + `) +}) + +test('enabled', () => { + expect(myActionQueryOptions(config, { requiredParam: 'value' }).enabled).toBe(true) +}) + +test('queryFn: calls query fn', async () => { + const options = myActionQueryOptions(config, { requiredParam: 'value' }) + const result = await options.queryFn({ queryKey: options.queryKey } as any) + expect(result).toMatchInlineSnapshot(`...`) +}) +``` + +--- + +## 3. Framework Bindings + +### React Query Hook + +```ts +'use client' +import type { Config, MyActionErrorType, ResolvedRegister } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type MyActionData, + type MyActionOptions, + myActionQueryOptions, +} from '@wagmi/core/query' +import type { ConfigParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseMyActionParameters< + config extends Config = Config, + selectData = MyActionData, +> = Compute & ConfigParameter> + +export type UseMyActionReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useMyAction */ +export function useMyAction< + config extends Config = ResolvedRegister['config'], + selectData = MyActionData, +>( + parameters: UseMyActionParameters = {}, +): UseMyActionReturnType { + const config = useConfig(parameters) + const chainId = useChainId({ config }) + const options = myActionQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + query: parameters.query, + }) + return useQuery(options) +} +``` + +### React Mutation Hook + +```ts +'use client' +import { useMutation } from '@tanstack/react-query' +import type { Config, ResolvedRegister, MyActionErrorType } from '@wagmi/core' +import { + type MyActionData, + type MyActionMutate, + type MyActionMutateAsync, + type MyActionOptions, + type MyActionVariables, + myActionMutationOptions, +} from '@wagmi/core/query' +import type { ConfigParameter } from '../types/properties.js' +import type { UseMutationReturnType } from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseMyActionParameters = + MyActionOptions & ConfigParameter + +export type UseMyActionReturnType = + UseMutationReturnType< + MyActionData, + MyActionErrorType, + MyActionVariables, + context, + MyActionMutate, + MyActionMutateAsync + > + +/** https://wagmi.sh/react/api/hooks/useMyAction */ +export function useMyAction< + config extends Config = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseMyActionParameters = {}, +): UseMyActionReturnType { + const config = useConfig(parameters) + const options = myActionMutationOptions(config, parameters) + const mutation = useMutation(options) + type Return = UseMyActionReturnType + return { + ...mutation, + mutate: mutation.mutate as Return['mutate'], + mutateAsync: mutation.mutateAsync as Return['mutateAsync'], + } +} +``` + +### Vue Composable (Query) + +```ts +import type { Config, MyActionErrorType, ResolvedRegister } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type MyActionData, + type MyActionOptions, + myActionQueryOptions, +} from '@wagmi/core/query' +import { computed } from 'vue' +import type { ConfigParameter } from '../types/properties.js' +import type { DeepMaybeRef } from '../types/ref.js' +import { deepUnref } from '../utils/cloneDeep.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseMyActionParameters< + config extends Config = Config, + selectData = MyActionData, +> = Compute & ConfigParameter>> + +export type UseMyActionReturnType = + UseQueryReturnType + +/** https://wagmi.sh/vue/api/composables/useMyAction */ +export function useMyAction< + config extends Config = ResolvedRegister['config'], + selectData = MyActionData, +>( + parameters: UseMyActionParameters = {}, +): UseMyActionReturnType { + const params = computed(() => deepUnref(parameters)) + const config = useConfig(params) + const chainId = useChainId({ config }) + const options = computed(() => + myActionQueryOptions(config as any, { + ...params.value, + chainId: params.value.chainId ?? chainId.value, + query: params.value.query, + }), + ) + return useQuery(options as any) as any +} +``` + +### Framework Rules + +| Rule | React | Vue | +|------|-------|-----| +| Top directive | `'use client'` | None | +| Parameters wrapper | `Compute<...>` | `Compute>` | +| Reactivity | Direct | `computed()` + `deepUnref()` | +| Doc URL | `wagmi.sh/react/api/hooks/` | `wagmi.sh/vue/api/composables/` | + +**Shared rules:** +- `ResolvedRegister['config']`: Use in function signature only, not type defs +- No `enabled`/`structuralSharing` in hooks: Handled by queryOptions + +### Testing + +**Query hook test-d.ts**: +```ts +import { abi } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' +import { useMyAction } from './useMyAction.js' + +test('select data', () => { + const result = useMyAction({ + /* params */ + query: { + select(data) { + expectTypeOf(data).toEqualTypeOf() + return data + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) +``` + +**Mutation hook test-d.ts**: +```ts +import { expectTypeOf, test } from 'vitest' +import { useMyAction } from './useMyAction.js' + +test('context', () => { + const { mutate } = useMyAction({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toMatchTypeOf<{ /* expected shape */ }>() + return { foo: 'bar' } + }, + onError(error, variables, context) { /* test types */ }, + onSuccess(data, variables, context) { /* test types */ }, + onSettled(data, error, variables, context) { /* test types */ }, + }, + }) + + mutate({ /* params */ }, { + onSuccess(data, variables, context) { /* test inference */ }, + }) +}) +``` + +--- + +## Exports + +Add to `exports/index.ts` in respective package: + +```ts +// packages/core/src/exports/index.ts +export { + type MyActionParameters, + type MyActionReturnType, + type MyActionErrorType, + myAction, +} from '../actions/myAction.js' + +// packages/core/src/exports/query.ts +export { + type MyActionData, + type MyActionOptions, + type MyActionQueryFnData, + type MyActionQueryKey, + type MyActionQueryOptions, + myActionQueryKey, + myActionQueryOptions, +} from '../query/myAction.js' + +// packages/react/src/exports/index.ts +export { + type UseMyActionParameters, + type UseMyActionReturnType, + useMyAction, +} from '../hooks/useMyAction.js' +``` + +--- + +## Verification + +```bash +# Format +pnpm format + +# Type check (all or filtered) +pnpm check:types +pnpm --filter @wagmi/core check:types +pnpm --filter wagmi check:types + +# Test (all or filtered) +pnpm test +pnpm test --project core +pnpm test --project react + +# Update test snapshots +pnpm vitest -u + +# Type benchmarks +pnpm bench:types + +# Viem version mismatch in test snapshots +pnpm version:update:viem + +# Build (all or filtered) +pnpm run clean && pnpm build +pnpm --filter @wagmi/core build +``` diff --git a/.changeset/angry-islands-attack.md b/.changeset/angry-islands-attack.md new file mode 100644 index 0000000000..4ef09b64f2 --- /dev/null +++ b/.changeset/angry-islands-attack.md @@ -0,0 +1,8 @@ +--- +"wagmi": minor +"@wagmi/solid": minor +"@wagmi/core": minor +"@wagmi/vue": minor +--- + +Bumped Tanstack Query Packages to v5.90.x diff --git a/.changeset/config.json b/.changeset/config.json index c47279e4c8..11187a0aca 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,9 +1,15 @@ { - "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", + "$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json", + "changelog": [ + "@svitejs/changesets-changelog-github-compact", + { "repo": "wevm/wagmi" } + ], + "commit": false, "access": "public", + "fixed": [], + "linked": [], "baseBranch": "main", - "changelog": ["@changesets/changelog-github", { "repo": "wevm/wagmi" }], - "commit": false, + "updateInternalDependencies": "minor", "ignore": [ "*-register", "@wagmi/test", @@ -11,9 +17,5 @@ "next-app", "nuxt-app", "vite-*" - ], - "updateInternalDependencies": "patch", - "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { - "onlyUpdatePeerDependentsWhenOutOfRange": true - } + ] } diff --git a/.changeset/cool-masks-hang.md b/.changeset/cool-masks-hang.md deleted file mode 100644 index 0c0ff73c79..0000000000 --- a/.changeset/cool-masks-hang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@wagmi/connectors": patch ---- - -Added `rdns` property to Coinbase Wallet v3 connector diff --git a/.changeset/curvy-cherries-beg.md b/.changeset/curvy-cherries-beg.md new file mode 100644 index 0000000000..7bfc3b19e3 --- /dev/null +++ b/.changeset/curvy-cherries-beg.md @@ -0,0 +1,7 @@ +--- +"@nomicfoundation/hardhat-chai-matchers": patch +"hardhat": patch +"@nomicfoundation/hardhat-viem": patch +--- + +Improved loading performance diff --git a/.changeset/friendly-laws-move.md b/.changeset/friendly-laws-move.md new file mode 100644 index 0000000000..b018442b92 --- /dev/null +++ b/.changeset/friendly-laws-move.md @@ -0,0 +1,11 @@ +--- +'@wagmi/core': patch +'@wagmi/vue': patch +'wagmi': patch +--- + +Fix `feePayer` typing for regular wallet write actions on Tempo chains. + +This preserves chain-specific transaction request fields in `sendTransaction`, +`sendTransactionSync`, and `deployContract`, and adds type regression coverage +for core, React, and Vue surfaces. diff --git a/.changeset/loose-mirrors-yawn.md b/.changeset/loose-mirrors-yawn.md new file mode 100644 index 0000000000..44b21e5198 --- /dev/null +++ b/.changeset/loose-mirrors-yawn.md @@ -0,0 +1,5 @@ +--- +"@wagmi/cli": minor +--- + +Upgraded to Sourcify v2 API in `sourcify` plugin diff --git a/.changeset/new-elephants-travel.md b/.changeset/new-elephants-travel.md new file mode 100644 index 0000000000..ddfb37374a --- /dev/null +++ b/.changeset/new-elephants-travel.md @@ -0,0 +1,5 @@ +--- +"@wagmi/cli": patch +--- + +Updated block explorer chains. diff --git a/.changeset/nice-pandas-clap.md b/.changeset/nice-pandas-clap.md new file mode 100644 index 0000000000..7f4af53010 --- /dev/null +++ b/.changeset/nice-pandas-clap.md @@ -0,0 +1,5 @@ +--- + +--- + +Circleci project setup diff --git a/.changeset/quick-hairs-scream.md b/.changeset/quick-hairs-scream.md new file mode 100644 index 0000000000..206e94e246 --- /dev/null +++ b/.changeset/quick-hairs-scream.md @@ -0,0 +1,6 @@ +--- +"wagmi": patch +"@wagmi/core": patch +--- + +Added `chainId` parameter to `getCapabilities`/`useCapabilities`. diff --git a/.changeset/spicy-bats-juggle.md b/.changeset/spicy-bats-juggle.md new file mode 100644 index 0000000000..cf7a154229 --- /dev/null +++ b/.changeset/spicy-bats-juggle.md @@ -0,0 +1,6 @@ +--- +"@wagmi/cli": patch +"site": patch +--- + +Circleci project setup diff --git a/.changeset/tall-fans-mate.md b/.changeset/tall-fans-mate.md new file mode 100644 index 0000000000..cf7a154229 --- /dev/null +++ b/.changeset/tall-fans-mate.md @@ -0,0 +1,6 @@ +--- +"@wagmi/cli": patch +"site": patch +--- + +Circleci project setup diff --git a/.changeset/tiny-laws-dream.md b/.changeset/tiny-laws-dream.md new file mode 100644 index 0000000000..c39a3d68b9 --- /dev/null +++ b/.changeset/tiny-laws-dream.md @@ -0,0 +1,5 @@ +--- +"@fake-scope/fake-pkg": patch +--- + +Circleci project setup diff --git a/.changeset/twenty-olives-admire.md b/.changeset/twenty-olives-admire.md new file mode 100644 index 0000000000..6523b46c46 --- /dev/null +++ b/.changeset/twenty-olives-admire.md @@ -0,0 +1,5 @@ +--- +"@wagmi/cli": patch +--- + +Use Sourcify v2 API in sourcify plugin diff --git a/.changeset/two-spies-beam.md b/.changeset/two-spies-beam.md new file mode 100644 index 0000000000..e1d0e13eb0 --- /dev/null +++ b/.changeset/two-spies-beam.md @@ -0,0 +1,5 @@ +--- +"@wagmi/connectors": patch +--- + +Bumped MetaMask SDK version in accordance with [security advisory](https://github.com/advisories/GHSA-qj3p-xc97-xw74). diff --git a/.changeset/young-guests-care.md b/.changeset/young-guests-care.md new file mode 100644 index 0000000000..8de2292dde --- /dev/null +++ b/.changeset/young-guests-care.md @@ -0,0 +1,5 @@ +--- +"site": patch +--- + +docs(readme): fix typo diff --git a/.changeset/young-houses-relate.md b/.changeset/young-houses-relate.md deleted file mode 100644 index 288ab44256..0000000000 --- a/.changeset/young-houses-relate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@wagmi/connectors": patch ---- - -Bumped `@safe-global/safe-apps-provider` version to `0.18.6`. diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..d5d401c518 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,31 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000000..8a593f6ba2 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,99 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "sourceType": "module", + "project": [ + "./tsconfig.json", + "./references/tsconfig.json", + "./docs/tsconfig.json", + "./examples/*/tsconfig.json" + ] + }, + "plugins": ["@typescript-eslint", "import"], + "extends": [ + "eslint:recommended", + "plugin:eslint-comments/recommended", + "plugin:@typescript-eslint/recommended", + "plugin:import/recommended", + "plugin:import/typescript", + "plugin:react-hooks/recommended", + "plugin:testing-library/react", + "plugin:prettier/recommended", + "prettier" + ], + "rules": { + // `@typescript-eslint` + // https://github.com/typescript-eslint/typescript-eslint + "@typescript-eslint/consistent-type-assertions": "error", + "@typescript-eslint/consistent-type-exports": "error", + "@typescript-eslint/consistent-type-imports": "error", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unused-vars": [ + 2, + { + "argsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^_", + "destructuredArrayIgnorePattern": "^_", + "ignoreRestSiblings": true + } + ], + "@typescript-eslint/no-var-requires": "off", + // `eslint-plugin-import` + // https://github.com/benmosher/eslint-plugin-import + "import/order": [ + "error", + { + "groups": ["external", "internal"], + "newlines-between": "always-and-inside-groups", + "alphabetize": { + "order": "asc" + } + } + ], + "sort-imports": [ + "warn", + { + "ignoreCase": false, + "ignoreDeclarationSort": true, + "ignoreMemberSort": false + } + ] + }, + "overrides": [ + { + "files": "**/*.mdx/**", + "extends": ["plugin:mdx/recommended"], + "rules": { + "import/no-anonymous-default-export": "off", + "react/display-name": "off", + "react/jsx-no-undef": "off", + "no-undef": "off" + }, + "settings": { + "mdx/code-blocks": true + } + } + ], + "ignorePatterns": ["CHANGELOG.md", "build", "dist", "node_modules", "**/*.config.js", "**/*.config.mjs"], + "settings": { + "import/parsers": { + "@typescript-eslint/parser": [".ts", ".tsx", ".d.ts"] + }, + "import/resolver": { + "typescript": { + "alwaysTryTypes": true + } + }, + "react": { + "version": "detect" + } + }, + "env": { + "es6": true, + "browser": true, + "node": true + } +} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 12451d4bc9..0000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,5 +0,0 @@ -@tmm @jxom - -/packages/connectors/src/metaMask @ecp4224 @omridan159 @abretonc7s @elefantel @BjornGunnarsson @EdouardBougon -/packages/connectors/src/safe @DaniSomoza @dasanra @mikhailxyz @yagopv -/packages/connectors/src/walletConnect @ganchoradkov @glitch-txs @ignaciosantise @tomiir diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d3ab387e17..065515d7aa 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1 +1,191 @@ -[View Contributing Guide on wagmi.sh](https://wagmi.sh/dev/contributing) \ No newline at end of file +# Contributing + +Thanks for your interest in contributing to wagmi! Please take a moment to review this document **before submitting a pull request.** + +If you want to contribute, but aren't sure where to start, you can create a [new discussion](https://github.com/wagmi-dev/wagmi/discussions). + +> **Note** +> +> **Please ask first before starting work on any significant new features. This includes things like adding new connectors, hooks, chains, API providers, etc.** +> +> It's never a fun experience to have your pull request declined after investing time and effort into a new feature. To avoid this from happening, we request that contributors create a [feature request](https://github.com/wagmi-dev/wagmi/discussions/new?category=ideas) to first discuss any API changes or significant new ideas. + +
+ +## Basic guide + +This guide is intended to help you get started with contributing. By following these steps, you will understand the development process and workflow. + +1. [Cloning the repository](#cloning-the-repository) +2. [Installing Node.js and pnpm](#installing-nodejs-and-pnpm) +3. [Installing dependencies](#installing-dependencies) +4. [Starting the development playground](#starting-the-development-playground) +5. [Running the test suite](#running-the-test-suite) +6. [Writing documentation](#writing-documentation) +7. [Submitting a pull request](#submitting-a-pull-request) + +## Advanced guide + +This guide covers more advanced topics. Pick the topics based on your needs. + +8. [Versioning](#versioning) + +
+ +--- + +
+ +## Cloning the repository + +To start contributing to the project, clone it to your local machine using git: + +```bash +git clone https://github.com/wagmi-dev/wagmi.git --recurse-submodules +``` + +Or the [GitHub CLI](https://cli.github.com): + +```bash +gh repo clone wagmi-dev/wagmi -- --recurse-submodules +``` + + + +## Installing Node.js and pnpm + +wagmi uses [pnpm workspaces](https://pnpm.io/workspaces) to manage multiple projects. You need to install **Node.js v16 or higher** and **pnpm v7 or higher**. + +You can run the following commands in your terminal to check your local Node.js and npm versions: + +```bash +node -v +pnpm -v +``` + +If the versions are not correct or you don't have Node.js or pnpm installed, download and follow their setup instructions: + +- Install Node.js using [fnm](https://github.com/Schniz/fnm) or from the [official website](https://nodejs.org) +- Install [pnpm](https://pnpm.io/installation) + + + +## Installing dependencies + +Once in the project's root directory, run the following command to install the project's dependencies: + +```bash +pnpm install +``` + +After the install completes, pnpm links packages across the project for development and [git hooks](https://github.com/toplenboren/simple-git-hooks) are set up. + + + +## Starting the development playground + +To start the local development playground, run the following. This will run a [Next.js](https://nextjs.org) app (located at [`examples/_dev`](../examples/_dev)) that is set up for playing around with code while making changes. + +```bash +pnpm playground +``` + +Once the Next.js dev server is running, you can make changes to any of the package source files (e.g. `packages/react`) and it will automatically update the playground. (If the playground isn't automatically updating, try running `pnpm dev` to relink packages in development mode.) + + + +## Running the test suite + +wagmi uses [Anvil](https://github.com/foundry-rs/foundry/tree/master/anvil) to execute tests against a local Ethereum node. First, install Anvil via [Foundry](https://book.getfoundry.sh/getting-started/installation). Next, add the following to your environment (recommended to use [`direnv`](https://github.com/direnv/direnv)): + +```bash +ANVIL_FORK_URL=https://eth-mainnet.alchemyapi.io/v2/ +``` + +`ANVIL_FORK_URL` can be for any RPC service provider (e.g. Alchemy or Infura). Now you are ready to run the tests! In one terminal session, spin up Anvil using `pnpm anvil`. Next, in a different terminal session, you have the following options for running tests: + +- `pnpm test` — runs tests in watch mode +- `pnpm test:run` — performs single run without watch mode + +When adding new features or fixing bugs, it's important to add test cases to cover the new/updated behavior. If snapshot tests fail, you can run the `test:update` command to update the snapshots. + + + +## Writing documentation + +Documentation is crucial to helping developers of all experience levels use wagmi. wagmi uses [Nextra](https://github.com/shuding/nextra) and [MDX](https://mdxjs.com) for the documentation site (located at [`docs`](../docs)). To start the site in dev mode, run: + +```bash +pnpm docs:dev +``` + +Try to keep documentation brief and use plain language so folks of all experience levels can understand. If you think something is unclear or could be explained better, you are welcome to open a pull request. + + + +## Submitting a pull request + +When you're ready to submit a pull request, you can follow these naming conventions: + +- Pull request titles use the [Imperative Mood](https://en.wikipedia.org/wiki/Imperative_mood) (e.g., `Add something`, `Fix something`). +- [Changesets](#versioning) use past tense verbs (e.g., `Added something`, `Fixed something`). + +When you submit a pull request, GitHub will automatically lint, build, and test your changes. If you see an ❌, it's most likely a bug in your code. Please, inspect the logs through the GitHub UI to find the cause. + + + +
+ +--- + +
+ ✅ Now you're ready to contribute to wagmi! Follow the next steps if you need more advanced instructions. +
+ +--- + +
+ +## Versioning + +When adding new features or fixing bugs, we'll need to bump the package versions. We use [Changesets](https://github.com/changesets/changesets) to do this. + +> **Note** +> +> Only changes to the codebase that affect the public API or existing behavior (e.g. bugs) need changesets. + +Each changeset defines which package(s) should be published and whether the change should be a major/minor/patch release, as well as providing release notes that will be added to the changelog upon release. + +To create a new changeset, run `pnpm changeset`. This will run the Changesets CLI, prompting you for details about the change. You’ll be able to edit the file after it’s created — don’t worry about getting everything perfect up front. + +Since we’re currently in beta, all changes should be marked as a minor/patch release to keep us within the `v0.x` range. + +Even though you can technically use any markdown formatting you like, headings should be avoided since each changeset will ultimately be nested within a bullet list. Instead, bold text should be used as section headings. + +If your PR is making changes to an area that already has a changeset (e.g. there’s an existing changeset covering theme API changes but you’re making further changes to the same API), you should update the existing changeset in your PR rather than creating a new one. + +### Releasing + +The first time a PR with a changeset is merged after a release, a new PR will automatically be created called `chore: version packages`. Any subsequent PRs with changesets will automatically update this existing version packages PR. Merging this PR triggers the release process by publishing to npm and cleaning up the changeset files. + +### Creating a snapshot release + +If a PR has changesets, you can create a [snapshot release](https://github.com/changesets/changesets/blob/main/docs/snapshot-releases.md) by [manually dispatching](https://github.com/wagmi-dev/wagmi/actions/workflows/snapshot.yml) the Snapshot workflow. This publishes a tagged version to npm with the PR branch name and timestamp. + + diff --git a/.github/DISCUSSION_TEMPLATE/connector-request.yml b/.github/DISCUSSION_TEMPLATE/connector-request.yml deleted file mode 100644 index c1e31b1b6b..0000000000 --- a/.github/DISCUSSION_TEMPLATE/connector-request.yml +++ /dev/null @@ -1,51 +0,0 @@ -title: '[Connector Request] ' -body: - - type: markdown - attributes: - value: | - Thanks for your interest in contributing a new Connector to the Wagmi! If you haven't already, please read the [Contributing Guidelines](https://wagmi.sh/dev/contributing). Once you submit the form, the Wagmi team will follow up in the discussion thread to discuss next steps. - - Please note that in order for connector requests to be accepted, the team creating the Connector must [sponsor Wagmi](https://github.com/sponsors/wevm). It takes time and effort to maintain third-party connectors. Wagmi is an OSS project that depends on sponsors and grants to continue our work. Please get in touch via [dev@wevm.dev](mailto:dev@wevm.dev) if you have questions about sponsoring. - - - type: textarea - attributes: - label: What **novel use-case** does the Connector provide? - description: | - A novel use-case is likely one that is not already covered by or not easily extended from another Connector (such as the `injected` or `walletConnect`). - - Examples of **novel** use-cases could be a connector that integrates with: - - - the injected `window.ethereum` provider (a la `injected`) - - a series of wallets via QR Codes or Mobile Deep Links (a la `walletConnect`) - - a wallet with it's own SDK (a la `coinbaseWallet`) - - hardware wallet(s) via Web USB/Bluetooth - - an Externally Owned Account via a private key or some other method - - Examples of **nonnovel** use-cases would be a connector that: - - - extends another connector (e.g. `walletConnect`) with no significant differences in functionality other than branding, etc. - placeholder: Info on what makes this connector different. - validations: - required: true - - - type: textarea - attributes: - label: Are the Connector's integrations production-ready and generally available? - description: Connectors are intended to be used by consumers in production as part of Wagmi. As such, the Connector and all dependencies must be production-ready and generally available. This means your connector should not rely on non-production software or be restricted to a limited group of users. For example, if your connector requires a wallet that has a closed beta, it is not ready for inclusion in Wagmi. - placeholder: Info about the Connector and any dependencies (e.g. browser extension, wallet app, npm package). - validations: - required: true - - - type: checkboxes - attributes: - label: Are you committed to actively maintaining the Connector? - description: It is critical connectors are updated in a timely manner and actively maintained so that users of Wagmi can rely on them in production settings. The Wagmi core team will provide as much assistance as possible to keep connectors up-to-date with breaking changes from Wagmi, but it is your responsibility to ensure that any dependencies and issues/discussions related to the Connector are handled in a timely manner. If this is not done, the Connector could be removed from the future versions. - options: - - label: Yes, my team is or I am committed to actively maintaining the Connector. - required: true - - - type: textarea - attributes: - label: Additional comments - description: Feel free to jot down any additional info you think might be helpful. - placeholder: Additional comments, questions, feedback. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..d2eb2adc8f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '[BUG] ' +labels: 'bug' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior. A link to a minimal reproduction is highly recommended. You can use a service like [CodeSandbox](https://codesandbox.io/p/sandbox/wagmi-reproduction-template-15n57l) or [StackBlitz](https://stackblitz.com/fork/wagmi-reproduction-template). + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**System Info** + - `wagmi` version: + - `viem` version: + - OS: [e.g. Windows, macOS, Ubuntu] + - Browser: [e.g. chrome, safari] + - Framework: [e.g. Next.js, Vite, etc.] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 8a561abba1..ae99b344e6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,88 +1,65 @@ name: Bug Report -description: Report bugs or issues. +description: File a bug/issue +title: 'bug: ' body: - type: markdown attributes: value: | - Thanks for taking the time to fill out this bug report! The more info you provide, the more we can help you. - - If you are a [Wagmi Sponsor](https://github.com/sponsors/wevm?metadata_campaign=gh_issue), your issues are prioritized. + Thanks for taking the time to fill out this bug report! The more info you provide, the more we can help you. If you are a [Sponsor](https://github.com/sponsors/wagmi-dev?metadata_campaign=gh_issue), your issues are prioritized. - type: checkboxes attributes: - label: Check existing issues - description: By submitting this issue, you checked there isn't [already an issue](https://github.com/wevm/wagmi/issues) for this bug. + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. options: - - label: I checked there isn't [already an issue](https://github.com/wevm/wagmi/issues) for the bug I encountered. + - label: I have searched the existing issues required: true - - type: textarea + - type: input attributes: - label: Describe the bug - description: Clear and concise description of the bug. If you intend to submit a PR for this issue, tell us in the description. Thanks! - placeholder: I am doing… What I expect is… What is actually happening… + label: Package Version + description: What version of wagmi are you using? + placeholder: 1.0.0 validations: required: true - - type: input - id: reproduction + - type: textarea attributes: - label: Link to Minimal Reproducible Example - description: "Please provide a link that can reproduce the problem: [new.wagmi.sh](https://new.wagmi.sh) for runtime issues or [TypeScript Playground](https://www.typescriptlang.org/play) for type issues. For most issues, you will likely get asked to provide a minimal reproducible example so why not add one now :) If a report is vague (e.g. just snippets, generic error message, screenshot, etc.) and has no reproduction, it will receive a \"Needs Reproduction\" label and be auto-closed." - placeholder: https://new.wagmi.sh + label: Current Behavior + description: A concise description of what you're experiencing. validations: required: false - type: textarea attributes: - label: Steps To Reproduce - description: Steps or code snippets to reproduce the behavior. + label: Expected Behavior + description: A concise description of what you expected to happen. validations: required: false - - type: dropdown - attributes: - label: What Wagmi package(s) are you using? - multiple: true - options: - - 'wagmi' - - '@wagmi/cli' - - '@wagmi/connectors' - - '@wagmi/core' - - '@wagmi/vue' - - 'create-wagmi' - validations: - required: true - - - type: input - attributes: - label: Wagmi Package(s) Version(s) - description: What version of the Wagmi packages you selected above are you using? If using multiple, separate with comma (e.g. `wagmi@x.y.z, @wagmi/cli@x.y.z`). - placeholder: x.y.z (do not write `latest`) - validations: - required: true - - - type: input + - type: textarea attributes: - label: Viem Version - description: What version of [Viem](https://viem.sh) are you using? - placeholder: x.y.z (do not write `latest`) + label: Steps To Reproduce + description: Steps or code snippets to reproduce the behavior. validations: - required: true + required: false - type: input attributes: - label: TypeScript Version - description: What version of TypeScript are you using? Wagmi requires `typescript@>=5`. - placeholder: x.y.z (do not write `latest`) + label: Link to Minimal Reproducible Example (StackBlitz, CodeSandbox, GitHub repo etc.) + description: | + Please provide a link via [new.wagmi.sh](https://new.wagmi.sh/) or a link to a minimal repository that can reproduce the problem you ran into. `npm create wagmi@latest` can also be used as a starter template. + This makes investigating issues and helping you out significantly easier! For most issues, you will likely get asked to provide one so why not add one now :) + placeholder: https://new.wagmi.sh/ validations: required: false - type: textarea attributes: label: Anything else? - description: Anything that will give us more context about the issue you are encountering. Framework version (e.g. React, Vue), app framework (e.g. Next.js, Nuxt), bundler, etc. + description: | + Browser info? Screenshots? Anything that will give us more context about the issue you are encountering! + + Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. validations: required: false - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index fc8027c871..bcbc79e6cc 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,14 +1,8 @@ blank_issues_enabled: false contact_links: - - name: Get Help - url: https://github.com/wevm/wagmi/discussions/new?category=q-a - about: Ask a question and discuss with other community members. - - - name: Feature Request - url: https://github.com/wevm/wagmi/discussions/new?category=ideas - about: Request features or brainstorm ideas for new functionality. - - - name: Connector Request - url: https://github.com/wevm/wagmi/discussions/new?category=connector-request - about: Kick off a request for a new connector - + - name: Ask Question + url: https://github.com/wagmi-dev/wagmi/discussions/new?category=q-a + about: Ask questions and discuss with other community members + - name: Request Feature + url: https://github.com/wagmi-dev/wagmi/discussions/new?category=ideas + about: Requests features or brainstorm ideas for new functionality diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000..48d5f81fa4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/docs_issue.yml b/.github/ISSUE_TEMPLATE/docs_issue.yml deleted file mode 100644 index f2d53b8a98..0000000000 --- a/.github/ISSUE_TEMPLATE/docs_issue.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Documentation Issue -description: Tell us about missing or incorrect documentation. -labels: ['Area: Docs'] -body: - - type: markdown - attributes: - value: | - Thank you for submitting a documentation request. It helps make Wagmi better. - - If it's a small change, like misspelling or example that needs updating, feel free to submit a PR instead of creating this issue. - - - type: dropdown - attributes: - label: What is the type of issue? - multiple: true - options: - - Documentation is missing - - Documentation is incorrect - - Documentation is confusing - - Example code is not working - - Something else - - - type: textarea - attributes: - label: What is the issue? - validations: - required: true - - - type: textarea - attributes: - label: Where did you find it? - description: Please provide the URL(s) where you found this issue. - validations: - required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..bc1428bce5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '[FEAT] ' +labels: 'enhancement' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/README.md b/.github/README.md index 6b5f336419..f5673bd3e8 100644 --- a/.github/README.md +++ b/.github/README.md @@ -23,6 +23,19 @@ <img src="https://img.shields.io/npm/v/wagmi?colorA=f6f8fa&colorB=f6f8fa" alt="Version"> </picture> </a> + <a href="https://scorecard.dev/viewer/?uri=github.com/ossf/scorecard"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/ossf-scorecard/github.com/wevm/wagmi?label=openssf+scorecard&style=flat&color=21262d&labelColor=21262d"> + <img src="https://img.shields.io/ossf-scorecard/github.com/wevm/wagmi?label=openssf+scorecard&style=flat&color=f6f8fa&labelColor=f6f8fa" alt="OpenSSF Best Practices"> + </picture> + </a> + <a href="https://www.bestpractices.dev/en/projects/11233"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/openssf_best_practices-passing-21262d?labelColor=21262d"> + <img src="https://img.shields.io/badge/openssf_best_practices-passing-f6f8fa?labelColor=f6f8fa" alt="OpenSSF Best Practices"> + </picture> + </a> + <br /> <a href="https://github.com/wevm/wagmi/blob/main/LICENSE"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/npm/l/wagmi?colorA=21262d&colorB=21262d"> @@ -80,10 +93,10 @@ If you find Wagmi useful or use it for work, please consider [sponsoring Wagmi]( <img alt="paradigm logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/paradigm-light.svg" width="auto" height="70"> </picture> </a> - <a href="https://ithaca.xyz"> + <a href="https://tempo.xyz"> <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/ithaca-dark.svg"> - <img alt="ithaca logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/ithaca-light.svg" width="auto" height="70"> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/tempo-dark.svg"> + <img alt="tempo logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/tempo-light.svg" width="auto" height="70"> </picture> </a> </p> @@ -101,18 +114,6 @@ If you find Wagmi useful or use it for work, please consider [sponsoring Wagmi]( <img alt="context logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/context-light.svg" width="auto" height="50"> </picture> </a> - <a href="https://walletconnect.com"> - <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/walletconnect-dark.svg"> - <img alt="WalletConnect logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/walletconnect-light.svg" width="auto" height="50"> - </picture> - </a> - <a href="https://twitter.com/prtyDAO"> - <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/partydao-dark.svg"> - <img alt="PartyDAO logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/partydao-light.svg" width="auto" height="50"> - </picture> - </a> <a href="https://dynamic.xyz"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/dynamic-dark.svg"> @@ -143,18 +144,6 @@ If you find Wagmi useful or use it for work, please consider [sponsoring Wagmi]( <img alt="pancake logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/pancake-light.svg" width="auto" height="50"> </picture> </a> - <a href="https://celo.org"> - <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/celo-dark.svg"> - <img alt="celo logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/celo-light.svg" width="auto" height="50"> - </picture> - </a> - <a href="https://rainbow.me"> - <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/rainbow-dark.svg"> - <img alt="rainbow logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/rainbow-light.svg" width="auto" height="50"> - </picture> - </a> <a href="https://pimlico.io"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/pimlico-dark.svg"> @@ -167,58 +156,16 @@ If you find Wagmi useful or use it for work, please consider [sponsoring Wagmi]( <img alt="zora logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/zora-light.svg" width="auto" height="50"> </picture> </a> - <a href="https://lattice.xyz"> - <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/lattice-dark.svg"> - <img alt="lattice logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/lattice-light.svg" width="auto" height="50"> - </picture> - </a> - <a href="https://twitter.com/supafinance"> - <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/supa-dark.svg"> - <img alt="supa logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/supa-light.svg" width="auto" height="50"> - </picture> - </a> - <a href="https://zksync.io"> - <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/zksync-dark.svg"> - <img alt="zksync logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/zksync-light.svg" width="auto" height="50"> - </picture> - </a> <a href="https://syndicate.io"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/syndicate-dark.svg"> <img alt="syndicate logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/syndicate-light.svg" width="auto" height="50"> </picture> </a> - <a href="https://reservoir.tools"> - <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/reservoir-dark.svg"> - <img alt="reservoir logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/reservoir-light.svg" width="auto" height="50"> - </picture> - </a> - <a href="https://linea.build"> - <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/linea-dark.svg"> - <img alt="linea logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/linea-light.svg" width="auto" height="50"> - </picture> - </a> - <a href="https://uniswap.org"> - <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/uniswap-dark.svg"> - <img alt="uniswap logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/uniswap-light.svg" width="auto" height="50"> - </picture> - </a> - <a href="https://biconomy.io"> - <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/b0276d897be98a4c94ad1d1c72ce99a1020eeb58/content/sponsors/biconomy-dark.svg"> - <img alt="biconomy logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/biconomy-light.svg" width="auto" height="50"> - </picture> - </a> - <a href="https://thirdweb.com"> + <a href="https://relay.link"> <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/thirdweb-dark.svg"> - <img alt="thirdweb logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/thirdweb-light.svg" width="auto" height="50"> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/relay-dark.svg"> + <img alt="relay logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/relay-light.svg" width="auto" height="50"> </picture> </a> <a href="https://polymarket.com"> @@ -227,18 +174,18 @@ If you find Wagmi useful or use it for work, please consider [sponsoring Wagmi]( <img alt="polymarket logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/polymarket-light.svg" width="auto" height="50"> </picture> </a> - <a href="https://routescan.io"> - <picture> - <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/routescan-dark.svg"> - <img alt="routescan logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/routescan-light.svg" width="auto" height="50"> - </picture> - </a> <a href="https://sequence.xyz"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/sequence-dark.svg"> <img alt="sequence logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/sequence-light.svg" width="auto" height="50"> </picture> </a> + <a href="https://web3auth.io"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/web3auth-dark.svg"> + <img alt="web3auth logo" src="https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/web3auth-light.svg" width="auto" height="50"> + </picture> + </a> </p> [Sponsor Wagmi](https://github.com/sponsors/wevm?metadata_campaign=gh_readme_support_bottom) diff --git a/.github/SECURITY.md b/.github/SECURITY.md deleted file mode 100644 index 54f40f38df..0000000000 --- a/.github/SECURITY.md +++ /dev/null @@ -1,6 +0,0 @@ -# Security Policy - -## Reporting a Vulnerability - -Contact [dev@wevm.dev](mailto:dev@wevm.dev). - diff --git a/.github/logo-dark.svg b/.github/logo-dark.svg deleted file mode 100644 index 5d47cce337..0000000000 --- a/.github/logo-dark.svg +++ /dev/null @@ -1,27 +0,0 @@ -<svg - viewBox="0 0 561 132" - fill="none" - xmlns="http://www.w3.org/2000/svg" -> - <path - d="M561 12C561 18.6274 555.627 24 549 24C542.373 24 537 18.6274 537 12C537 5.37259 542.373 0 549 0C555.627 0 561 5.37259 561 12Z" - fill="#f3f4f6" - /> - <path - d="M414 105C418.971 105 423 100.971 423 96V60C423 55.0294 427.029 51 432 51H450C454.971 51 459 55.0294 459 60V96C459 100.971 463.029 105 468 105C472.971 105 477 100.971 477 96V60C477 55.0294 481.029 51 486 51H504C508.971 51 513 55.0294 513 60V96C513 100.971 517.029 105 522 105H549C553.971 105 558 100.971 558 96V42C558 37.0294 553.971 33 549 33C544.029 33 540 37.0294 540 42V82.5C540 84.9853 537.985 87 535.5 87C533.015 87 531 84.9853 531 82.5V42C531 37.0294 526.971 33 522 33H414C409.029 33 405 37.0294 405 42V96C405 100.971 409.029 105 414 105Z" - fill="#f3f4f6" - /> - <path - fill-rule="evenodd" - clip-rule="evenodd" - d="M27 87C22.0294 87 18 82.9706 18 78V42C18 37.0294 13.9706 33 9 33C4.02943 33 0 37.0294 0 42V96C0 100.971 4.02943 105 9 105H117C121.971 105 126 100.971 126 96V60C126 55.0294 130.029 51 135 51H238.5C240.985 51 243 53.0147 243 55.5C243 57.9853 240.985 60 238.5 60H144C139.029 60 135 64.0294 135 69V96C135 100.971 139.029 105 144 105H252C256.971 105 261 100.971 261 96V42C261 37.0294 256.971 33 252 33H117C112.029 33 108 37.0294 108 42V78C108 82.9706 103.971 87 99 87H81C76.0294 87 72 82.9706 72 78V42C72 37.0294 67.9706 33 63 33C58.0294 33 54 37.0294 54 42V78C54 82.9706 49.9706 87 45 87H27ZM243 82.5C243 84.9853 240.985 87 238.5 87H157.5C155.015 87 153 84.9853 153 82.5C153 80.0147 155.015 78 157.5 78H238.5C240.985 78 243 80.0147 243 82.5Z" - fill="#f3f4f6" - /> - <path - fill-rule="evenodd" - clip-rule="evenodd" - d="M270 96C270 100.971 274.029 105 279 105H373.5C375.985 105 378 107.015 378 109.5C378 111.985 375.985 114 373.5 114H279C274.029 114 270 118.029 270 123C270 127.971 274.029 132 279 132H387C391.971 132 396 127.971 396 123V42C396 37.0294 391.971 33 387 33H279C274.029 33 270 37.0294 270 42V96ZM297 51C292.029 51 288 55.0294 288 60V78C288 82.9706 292.029 87 297 87H369C373.971 87 378 82.9706 378 78V60C378 55.0294 373.971 51 369 51H297Z" - fill="#f3f4f6" - /> -</svg> - diff --git a/.github/logo-light.svg b/.github/logo-light.svg deleted file mode 100644 index 4e28590c36..0000000000 --- a/.github/logo-light.svg +++ /dev/null @@ -1,27 +0,0 @@ -<svg - viewBox="0 0 561 132" - fill="none" - xmlns="http://www.w3.org/2000/svg" -> - <path - d="M561 12C561 18.6274 555.627 24 549 24C542.373 24 537 18.6274 537 12C537 5.37259 542.373 0 549 0C555.627 0 561 5.37259 561 12Z" - fill="#1B1B1B" - /> - <path - d="M414 105C418.971 105 423 100.971 423 96V60C423 55.0294 427.029 51 432 51H450C454.971 51 459 55.0294 459 60V96C459 100.971 463.029 105 468 105C472.971 105 477 100.971 477 96V60C477 55.0294 481.029 51 486 51H504C508.971 51 513 55.0294 513 60V96C513 100.971 517.029 105 522 105H549C553.971 105 558 100.971 558 96V42C558 37.0294 553.971 33 549 33C544.029 33 540 37.0294 540 42V82.5C540 84.9853 537.985 87 535.5 87C533.015 87 531 84.9853 531 82.5V42C531 37.0294 526.971 33 522 33H414C409.029 33 405 37.0294 405 42V96C405 100.971 409.029 105 414 105Z" - fill="#1B1B1B" - /> - <path - fill-rule="evenodd" - clip-rule="evenodd" - d="M27 87C22.0294 87 18 82.9706 18 78V42C18 37.0294 13.9706 33 9 33C4.02943 33 0 37.0294 0 42V96C0 100.971 4.02943 105 9 105H117C121.971 105 126 100.971 126 96V60C126 55.0294 130.029 51 135 51H238.5C240.985 51 243 53.0147 243 55.5C243 57.9853 240.985 60 238.5 60H144C139.029 60 135 64.0294 135 69V96C135 100.971 139.029 105 144 105H252C256.971 105 261 100.971 261 96V42C261 37.0294 256.971 33 252 33H117C112.029 33 108 37.0294 108 42V78C108 82.9706 103.971 87 99 87H81C76.0294 87 72 82.9706 72 78V42C72 37.0294 67.9706 33 63 33C58.0294 33 54 37.0294 54 42V78C54 82.9706 49.9706 87 45 87H27ZM243 82.5C243 84.9853 240.985 87 238.5 87H157.5C155.015 87 153 84.9853 153 82.5C153 80.0147 155.015 78 157.5 78H238.5C240.985 78 243 80.0147 243 82.5Z" - fill="#1B1B1B" - /> - <path - fill-rule="evenodd" - clip-rule="evenodd" - d="M270 96C270 100.971 274.029 105 279 105H373.5C375.985 105 378 107.015 378 109.5C378 111.985 375.985 114 373.5 114H279C274.029 114 270 118.029 270 123C270 127.971 274.029 132 279 132H387C391.971 132 396 127.971 396 123V42C396 37.0294 391.971 33 387 33H279C274.029 33 270 37.0294 270 42V96ZM297 51C292.029 51 288 55.0294 288 60V78C288 82.9706 292.029 87 297 87H369C373.971 87 378 82.9706 378 78V60C378 55.0294 373.971 51 369 51H297Z" - fill="#1B1B1B" - /> -</svg> - diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 602a32d0a8..e0f29dc408 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,12 +1,9 @@ -<!-- What is this PR solving? Write a clear description or reference the issues it solves (e.g. `fixes #123`). What other alternatives have you explored? Are there any parts you think require more attention from reviewers? --> +## Description -<!---------------------------------------------------------------------- -Before creating the pull request, please make sure you do the following: +_Concise description of proposed changes_ -- Read the Contributing Guidelines at https://wagmi.sh/dev/contributing -- Check that there isn't already a PR that solves the problem the same way. If you find a duplicate, please help us review it. -- Update the corresponding documentation if needed. -- Include relevant tests that fail without this PR, but pass with it. +## Additional Information -Thank you for contributing to Wagmi! ------------------------------------------------------------------------> +- [ ] I read the [contributing docs](/wagmi-dev/wagmi/blob/main/.github/CONTRIBUTING.md) (if this is your first contribution) + +Your ENS/address: diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000000..e9301ec4f6 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,17 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 180 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - bug + - not stale +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/.github/workflows/Foundry-project.yml b/.github/workflows/Foundry-project.yml new file mode 100644 index 0000000000..f0ca528491 --- /dev/null +++ b/.github/workflows/Foundry-project.yml @@ -0,0 +1,203 @@ +name: CI + +permissions: {} + +on: + pull_request: + merge_group: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: full + RUSTC_WRAPPER: "sccache" + +jobs: + test: + uses: ./.github/workflows/test.yml + permissions: + contents: read + with: + profile: default + secrets: inherit + + docs: + uses: ./.github/workflows/docs.yml + permissions: + contents: read + pages: write + id-token: write + secrets: inherit + + doctest: + runs-on: depot-ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + with: + toolchain: stable + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - run: cargo test --workspace --doc + + typos: + runs-on: depot-ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: crate-ci/typos@a1d64977b4aa1709d6328d518aa753f4899352d8 # v1 + + shellcheck: + runs-on: depot-ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - name: Shellcheck + shell: bash + run: ./.github/scripts/shellcheck.sh + + clippy: + runs-on: depot-ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + with: + toolchain: nightly + components: clippy + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - run: cargo clippy --workspace --all-targets --all-features + env: + RUSTFLAGS: -Dwarnings + + rustfmt: + runs-on: depot-ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + with: + toolchain: nightly + components: rustfmt + - run: cargo fmt --all --check + + forge-fmt: + runs-on: depot-ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + with: + toolchain: stable + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - name: forge fmt + shell: bash + run: ./.github/scripts/format.sh --check + + crate-checks: + runs-on: depot-ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + with: + toolchain: stable + - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: taiki-e/install-action@710817a1645ef40daad5bcde7431ceccf6cc3528 # v2 + with: + tool: cargo-hack + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - run: cargo hack check + + deny: + uses: tempoxyz/ci/.github/workflows/deny.yml@268b3ce142717ff86c58fbbcc3abc3f109f0fb8d # main + permissions: + contents: read + + codeql: + name: analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + permissions: + security-events: write + actions: read + contents: read + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" + + ci-success: + runs-on: ubuntu-latest + if: always() + permissions: {} + needs: + - test + - docs + - doctest + - typos + - clippy + - rustfmt + - forge-fmt + - crate-checks + - deny + - codeql + timeout-minutes: 30 + steps: + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1 + with: + jobs: ${{ toJSON(needs) }} diff --git a/.github/workflows/Vercel Preview Deployment.yml b/.github/workflows/Vercel Preview Deployment.yml new file mode 100644 index 0000000000..ca7ca97005 --- /dev/null +++ b/.github/workflows/Vercel Preview Deployment.yml @@ -0,0 +1,22 @@ +name: Playwright Tests + +on: + repository_dispatch: + types: + - 'vercel.deployment.success' +permissions: + contents: read +jobs: + run-e2es: + if: github.event_name == 'repository_dispatch' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.client_payload.git.sha }} + - name: Install dependencies + run: npm ci && npx playwright install --with-deps + - name: Run tests + run: npx playwright test + env: + BASE_URL: ${{ github.event.client_payload.url }} diff --git a/.github/workflows/cache.yml b/.github/workflows/cache.yml new file mode 100644 index 0000000000..a5cd0c36f8 --- /dev/null +++ b/.github/workflows/cache.yml @@ -0,0 +1,29 @@ +name: Bust Actions Cache + +on: + workflow_dispatch: + +permissions: + actions: write + +jobs: + bust-cache: + runs-on: ubuntu-latest + steps: + - name: Bust actions cache + uses: actions/github-script@v6 + with: + script: | + console.log("Clearing cache…") + const caches = await github.rest.actions.getActionsCacheList({ + owner: context.repo.owner, + repo: context.repo.repo, + }) + for (const cache of caches.data.actions_caches) { + github.rest.actions.deleteActionsCacheById({ + owner: context.repo.owner, + repo: context.repo.repo, + cache_id: cache.id, + }) + } + console.log("Cache cleared.") diff --git a/.github/workflows/changesets.yml b/.github/workflows/changesets.yml index 34e945ddf0..8599396c0c 100644 --- a/.github/workflows/changesets.yml +++ b/.github/workflows/changesets.yml @@ -10,31 +10,37 @@ concurrency: jobs: verify: name: Verify + permissions: + contents: write uses: ./.github/workflows/verify.yml secrets: inherit changesets: name: Publish needs: verify + # prevents this action from running on forks + if: github.repository == 'wevm/wagmi' permissions: - contents: write - id-token: write - pull-requests: write + contents: write # to create release (changesets/action) + id-token: write # OpenID Connect token needed for provenance + pull-requests: write # to create pull request (changesets/action) runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Clone repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 with: # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits fetch-depth: 0 - name: Install dependencies uses: wevm/actions/.github/actions/pnpm@main + with: + node-version: 24.5 - name: PR or publish - uses: changesets/action@06245a4e0a36c064a573d4150030f5ec548e4fcc + uses: changesets/action@e0145edc7d9d8679003495b11f87bd8ef63c0cba with: title: 'chore: version packages' commit: 'chore: version packages' @@ -43,18 +49,14 @@ jobs: version: pnpm changeset:version env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Publish prerelease if: steps.changesets.outputs.published != 'true' continue-on-error: true env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - npm config set "//registry.npmjs.org/:_authToken" "$NPM_TOKEN" git reset --hard origin/main - pnpm clean pnpm changeset version --no-git-tag --snapshot canary pnpm changeset:prepublish pnpm changeset publish --no-git-tag --snapshot canary --tag canary diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000000..d19e21b798 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,39 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, +# surfacing known-vulnerable versions of the packages declared or updated in the PR. +# Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable +# packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: 'Dependency review' +on: + pull_request: + branches: [ "main" ] + +# If using a dependency submission action in this workflow this permission will need to be set to: +# +# permissions: +# contents: write +# +# https://docs.github.com/en/enterprise-cloud@latest/code-security/supply-chain-security/understanding-your-software-supply-chain/using-the-dependency-submission-api +permissions: + contents: read + # Write permissions for pull-requests are required for using the `comment-summary-in-pr` option, comment out if you aren't using this option + pull-requests: write + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout repository' + uses: actions/checkout@v4 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v4 + # Commonly enabled options, see https://github.com/actions/dependency-review-action#configuration-options for all available options. + with: + comment-summary-in-pr: always + # fail-on-severity: moderate + # deny-licenses: GPL-1.0-or-later, LGPL-2.0-or-later + # retry-on-snapshot-warnings: true diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000000..e69ca5d4cd --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,93 @@ +# This workflow's name will appear on the GitHub Actions page. + +name: Deploy to Vercel +permissions: + contents: read + +# This specifies when the workflow should run. + +on: + + # The workflow will run on every 'push' to the 'main' branch. + + push: + branches: + - main + + # This allows you to manually trigger the workflow from the GitHub Actions page. + + workflow_dispatch: + +# These are environment variables passed to the Vercel CLI. +# They must be set as GitHub Secrets for security. + +env: + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} + + # Add any other environment variables your project needs here, + # such as API keys for the Coinbase SDK. + # Example: VITE_COINBASE_API_KEY: ${{ secrets.VITE_COINBASE_API_KEY }} + +# Defines a job named 'deploy'. + +jobs: + deploy: + # Specifies the runner environment for this job. + + runs-on: ubuntu-latest + + # The steps in the 'deploy' job. + steps: + # 1. Check out the project code from the repository. + # This is the first essential step to get access to your code. + - name: Checkout project code + uses: actions/checkout@v3 + + # 2. Set up Node.js. + # The version should match the one in your project. + steps: + - uses: actions/setup-node@v4 + - name: Setup Node.js + - uses: actions/setup-node@v4 + with: + node-version: 18.x,20.x,22.x + strategy: + matrix: + node-version: [18.x, 20.x, 22.x] + steps: + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + # 3. Install the Vercel CLI. + # The Vercel CLI is the main tool for interacting with the Vercel platform. + with: + node-version: 22.x + name: Install Vercel CLI + run: npm install --global vercel@latest + + # 4. Pull Vercel project settings. + # This fetches Vercel project links and Org ID. + with: + - name: Pull Vercel environment variables + run: vercel pull --yes --environment=production + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + + # 5. Build the project. + # This command builds your project for a production environment. + # It will automatically use the build command defined in your project's `package.json`. + with: + - name: Build project + run: vercel build + + # 6. Deploy the project to Vercel. + # This step deploys the pre-built code to Vercel. +strategy: + matrix: + node-version: [18.x, 20.x, 22.x] + name: Deploy to Vercel + run: vercel deploy --prebuilt + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} diff --git a/.github/workflows/issue-labeled.yml b/.github/workflows/issue-labeled.yml index 39b98291d1..eaa28bfd01 100644 --- a/.github/workflows/issue-labeled.yml +++ b/.github/workflows/issue-labeled.yml @@ -1,4 +1,6 @@ name: Issue Labeled +permissions: + issues: write on: issues: @@ -7,7 +9,7 @@ on: jobs: issue-labeled: if: ${{ github.repository_owner == 'wevm' }} - uses: wevm/actions/.github/workflows/issue-labeled.yml@main + uses: wevm/actions/.github/workflows/issue-labeled.yml@f7ad7f00e16e73322562922c241f21f0c7ffbbec with: needs-reproduction-body: | Hello @${{ github.event.issue.user.login }}. diff --git a/.github/workflows/jekyll-docker.yml b/.github/workflows/jekyll-docker.yml new file mode 100644 index 0000000000..c88a4430c3 --- /dev/null +++ b/.github/workflows/jekyll-docker.yml @@ -0,0 +1,23 @@ +name: Jekyll site CI + +permissions: + contents: read + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build the site in the jekyll/builder container + run: | + docker run \ + -v ${{ github.workspace }}:/srv/jekyll -v ${{ github.workspace }}/_site:/srv/jekyll/_site \ + jekyll/builder:latest /bin/bash -c "chmod -R 777 /srv/jekyll && jekyll build --future" diff --git a/.github/workflows/lock-issue.yml b/.github/workflows/lock-issue.yml index 279452d223..afce18e9fb 100644 --- a/.github/workflows/lock-issue.yml +++ b/.github/workflows/lock-issue.yml @@ -1,4 +1,6 @@ name: Lock Issue +permissions: + issues: write on: schedule: @@ -7,7 +9,7 @@ on: jobs: lock-issue: if: ${{ github.repository_owner == 'wevm' }} - uses: wevm/actions/.github/workflows/lock-issue.yml@main + uses: wevm/actions/.github/workflows/lock-issue.yml@f7ad7f00e16e73322562922c241f21f0c7ffbbec with: issue-comment: | This issue has been locked since it has been closed for more than 14 days. diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..9f87dd5d92 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,208 @@ +name: Main +permissions: + contents: read + +on: + pull_request: + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + install: + name: Install + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [19] + pnpm-version: [8] + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - uses: pnpm/action-setup@v2.2.4 + with: + version: ${{ matrix.pnpm-version }} + - name: Set up Node ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + cache: 'pnpm' + node-version: ${{ matrix.node-version }} + - name: Cache pnpm + uses: actions/cache@v3 + with: + path: ~/.pnpm-store + key: pnpm-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: pnpm- + - name: Cache node_modules + uses: actions/cache@v3 + id: cache-node-modules + with: + path: | + node_modules + docs/node_modules + examples/**/node_modules + packages/**/node_modules + packages/**/dist + references/packages/**/node_modules + key: modules-${{ hashFiles('pnpm-lock.yaml') }} + - name: Install Dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + run: pnpm i + - name: Link Dependencies + if: steps.cache-node-modules.outputs.cache-hit == 'true' + run: pnpm dev + + lint: + permissions: + contents: write + name: Lint + needs: install + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [19] + pnpm-version: [8] + steps: + - uses: actions/checkout@v3 + with: + submodules: true + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + - uses: pnpm/action-setup@v2.2.4 + with: + version: ${{ matrix.pnpm-version }} + - name: Set up Node ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + cache: 'pnpm' + node-version: ${{ matrix.node-version }} + - name: Cache node_modules + uses: actions/cache@v3 + with: + path: | + node_modules + docs/node_modules + examples/**/node_modules + packages/**/node_modules + packages/**/dist + references/packages/**/node_modules + key: modules-${{ hashFiles('pnpm-lock.yaml') }} + - name: Check types + run: pnpm typecheck + - name: Lint & format code + run: pnpm lint:fix && pnpm lint:format packages + - name: Commit + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + + git add . + if [ -z "$(git status --porcelain)" ]; then + echo "no formatting changed" + exit 0 + fi + git commit -m "chore: format" + git push + echo "pushed formatting changes https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)" + + build: + name: Build + permissions: + contents: read + needs: lint + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [19] + pnpm-version: [8] + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - uses: pnpm/action-setup@v2.2.4 + with: + version: ${{ matrix.pnpm-version }} + - name: Set up Node ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + cache: 'pnpm' + node-version: ${{ matrix.node-version }} + - name: Cache node_modules + uses: actions/cache@v3 + with: + path: | + node_modules + docs/node_modules + examples/**/node_modules + packages/**/node_modules + packages/**/dist + references/packages/**/node_modules + key: modules-${{ hashFiles('pnpm-lock.yaml') }} + - name: Build + run: pnpm build + + test: + name: Test + needs: lint + runs-on: ubuntu-latest + env: + ANVIL_BLOCK_NUMBER: 15578840 + strategy: + matrix: + node-version: [19] + pnpm-version: [8] + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - uses: pnpm/action-setup@v2.2.4 + with: + version: ${{ matrix.pnpm-version }} + - name: Set up Node ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + cache: 'pnpm' + node-version: ${{ matrix.node-version }} + - name: Cache node_modules + uses: actions/cache@v3 + with: + path: | + node_modules + docs/node_modules + examples/**/node_modules + packages/**/node_modules + packages/**/dist + references/packages/**/node_modules + key: modules-${{ hashFiles('pnpm-lock.yaml') }} + - name: Cache Anvil + uses: 'actions/cache@v3' + with: + path: ~/.foundry/cache/rpc/**/${{ env.ANVIL_BLOCK_NUMBER }} + key: foundry-anvil-${{ env.ANVIL_BLOCK_NUMBER }} + - name: Install rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: wasm32-unknown-unknown + profile: minimal + override: true + - name: Install Anvil + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + - name: Launch Anvil + run: anvil --fork-url $ANVIL_FORK_URL --fork-block-number $ANVIL_BLOCK_NUMBER & + env: + ANVIL_FORK_URL: ${{ secrets.ANVIL_FORK_URL }} + - name: Test `@wagmi/cli`, `@wagmi/core`, and `wagmi` + run: pnpm test:coverage + - name: Test types + run: pnpm test:typecheck + # Need to shutdown Anvil so cache gets created + - name: Shutdown Anvil + run: pkill -2 anvil diff --git a/.github/workflows/nextjs.yml b/.github/workflows/nextjs.yml new file mode 100644 index 0000000000..ed74736705 --- /dev/null +++ b/.github/workflows/nextjs.yml @@ -0,0 +1,93 @@ +# Sample workflow for building and deploying a Next.js site to GitHub Pages +# +# To get started with Next.js see: https://nextjs.org/docs/getting-started +# +name: Deploy Next.js site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Detect package manager + id: detect-package-manager + run: | + if [ -f "${{ github.workspace }}/yarn.lock" ]; then + echo "manager=yarn" >> $GITHUB_OUTPUT + echo "command=install" >> $GITHUB_OUTPUT + echo "runner=yarn" >> $GITHUB_OUTPUT + exit 0 + elif [ -f "${{ github.workspace }}/package.json" ]; then + echo "manager=npm" >> $GITHUB_OUTPUT + echo "command=ci" >> $GITHUB_OUTPUT + echo "runner=npx --no-install" >> $GITHUB_OUTPUT + exit 0 + else + echo "Unable to determine package manager" + exit 1 + fi + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: ${{ steps.detect-package-manager.outputs.manager }} + - name: Setup Pages + uses: actions/configure-pages@v5 + with: + # Automatically inject basePath in your Next.js configuration file and disable + # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized). + # + # You may remove this line if you want to manage the configuration yourself. + static_site_generator: next + - name: Restore cache + uses: actions/cache@v4 + with: + path: | + .next/cache + # Generate a new cache whenever packages or source files change. + key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} + # If source files changed but packages didn't, rebuild from a prior cache. + restore-keys: | + ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- + - name: Install dependencies + run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} + - name: Build with Next.js + run: ${{ steps.detect-package-manager.outputs.runner }} next build + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./out + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/nuxtjs.yml b/.github/workflows/nuxtjs.yml new file mode 100644 index 0000000000..7875b643ae --- /dev/null +++ b/.github/workflows/nuxtjs.yml @@ -0,0 +1,89 @@ +# Sample workflow for building and deploying a Nuxt site to GitHub Pages +# +# To get started with Nuxt see: https://nuxtjs.org/docs/get-started/installation +# +name: Deploy Nuxt site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Detect package manager + id: detect-package-manager + run: | + if [ -f "${{ github.workspace }}/yarn.lock" ]; then + echo "manager=yarn" >> $GITHUB_OUTPUT + echo "command=install" >> $GITHUB_OUTPUT + exit 0 + elif [ -f "${{ github.workspace }}/package.json" ]; then + echo "manager=npm" >> $GITHUB_OUTPUT + echo "command=ci" >> $GITHUB_OUTPUT + exit 0 + else + echo "Unable to determine package manager" + exit 1 + fi + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: ${{ steps.detect-package-manager.outputs.manager }} + - name: Setup Pages + uses: actions/configure-pages@v5 + with: + # Automatically inject router.base in your Nuxt configuration file and set + # target to static (https://nuxtjs.org/docs/configuration-glossary/configuration-target/). + # + # You may remove this line if you want to manage the configuration yourself. + static_site_generator: nuxt + - name: Restore Nuxt build cache + uses: actions/cache@v4 + with: + path: | + .nuxt + key: ${{ runner.os }}-nuxt-build-${{ hashFiles('**/package-lock.json', '**/yarn.lock', '**/pnpm-lock.yaml', 'package.json') }} + restore-keys: | + ${{ runner.os }}-nuxt-build- + - name: Install dependencies + run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} + - name: Static HTML export with Nuxt + run: ${{ steps.detect-package-manager.outputs.manager }} run generate + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./dist + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/octopusdeploy.yml b/.github/workflows/octopusdeploy.yml new file mode 100644 index 0000000000..9c4403d554 --- /dev/null +++ b/.github/workflows/octopusdeploy.yml @@ -0,0 +1,112 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by separate terms of service, +# privacy policy, and support documentation. +# +# This workflow will build and publish a Docker container which is then deployed through Octopus Deploy. +# +# The build job in this workflow currently assumes that there is a Dockerfile that generates the relevant application image. +# If required, this job can be modified to generate whatever alternative build artifact is required for your deployment. +# +# This workflow assumes you have already created a Project in Octopus Deploy. +# For instructions see https://octopus.com/docs/projects/setting-up-projects +# +# To configure this workflow: +# +# 1. Decide where you are going to host your image. +# This template uses the GitHub Registry for simplicity but if required you can update the relevant DOCKER_REGISTRY variables below. +# +# 2. Create and configure an OIDC credential for a service account in Octopus. +# This allows for passwordless authentication to your Octopus instance through a trust relationship configured between Octopus, GitHub and your GitHub Repository. +# https://octopus.com/docs/octopus-rest-api/openid-connect/github-actions +# +# 3. Configure your Octopus project details below: +# OCTOPUS_URL: update to your Octopus Instance Url +# OCTOPUS_SERVICE_ACCOUNT: update to your service account Id +# OCTOPUS_SPACE: update to the name of the space your project is configured in +# OCTOPUS_PROJECT: update to the name of your Octopus project +# OCTOPUS_ENVIRONMENT: update to the name of the environment to recieve the first deployment + + +name: 'Build and Deploy to Octopus Deploy' + +on: + push: + branches: + - '"main"' + +jobs: + build: + name: Build + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + env: + DOCKER_REGISTRY: ghcr.io # TODO: Update to your docker registry uri + DOCKER_REGISTRY_USERNAME: ${{ github.actor }} # TODO: Update to your docker registry username + DOCKER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} # TODO: Update to your docker registry password + outputs: + image_tag: ${{ steps.meta.outputs.version }} + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.DOCKER_REGISTRY }} + username: ${{ env.DOCKER_REGISTRY_USERNAME }} + password: ${{ env.DOCKER_REGISTRY_PASSWORD }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ${{ env.DOCKER_REGISTRY }}/${{ github.repository }} + tags: type=semver,pattern={{version}},value=v1.0.0-{{sha}} + + - name: Build and push Docker image + id: push + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + deploy: + name: Deploy + permissions: + id-token: write + runs-on: ubuntu-latest + needs: [ build ] + env: + OCTOPUS_URL: 'https://your-octopus-url' # TODO: update to your Octopus Instance url + OCTOPUS_SERVICE_ACCOUNT: 'your-service-account-id' # TODO: update to your service account Id + OCTOPUS_SPACE: 'your-space' # TODO: update to the name of the space your project is configured in + OCTOPUS_PROJECT: 'your-project' # TODO: update to the name of your Octopus project + OCTOPUS_ENVIRONMENT: 'your-environment' # TODO: update to the name of the environment to recieve the first deployment + + steps: + - name: Log in to Octopus Deploy + uses: OctopusDeploy/login@e485a40e4b47a154bdf59cc79e57894b0769a760 #v1.0.3 + with: + server: '${{ env.OCTOPUS_URL }}' + service_account_id: '${{ env.OCTOPUS_SERVICE_ACCOUNT }}' + + - name: Create Release + id: create_release + uses: OctopusDeploy/create-release-action@fea7e7b45c38c021b6bc5a14bd7eaa2ed5269214 #v3.2.2 + with: + project: '${{ env.OCTOPUS_PROJECT }}' + space: '${{ env.OCTOPUS_SPACE }}' + packages: '*:${{ needs.build.outputs.image_tag }}' + + - name: Deploy Release + uses: OctopusDeploy/deploy-release-action@b10a606c903b0a5bce24102af9d066638ab429ac #v3.2.1 + with: + project: '${{ env.OCTOPUS_PROJECT }}' + space: '${{ env.OCTOPUS_SPACE }}' + release_number: '${{ steps.create_release.outputs.release_number }}' + environments: ${{ env.OCTOPUS_ENVIRONMENT }} diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 9ff4c5bb76..6634c53d07 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -2,6 +2,8 @@ name: Pull Request on: pull_request: types: [opened, reopened, synchronize, ready_for_review] +permissions: + contents: read concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -10,23 +12,55 @@ concurrency: jobs: verify: name: Verify + permissions: + contents: write uses: ./.github/workflows/verify.yml secrets: inherit + publish: + name: Publish preview release + # prevents this action from running on forks + if: github.repository == 'wevm/wagmi' + permissions: + contents: read + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Clone repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 + + - name: Install dependencies + uses: wevm/actions/.github/actions/pnpm@f7ad7f00e16e73322562922c241f21f0c7ffbbec + with: + node-version: 24.5 + + - name: Publish to pkg.pr.new + run: | + pnpm changeset:prepublish + pnpx pkg-pr-new publish --pnpm --compact './packages/*' + env: + PKG_PR_NEW: true + size: name: Size + permissions: + contents: read + pull-requests: write runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Clone repository - uses: actions/checkout@v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 - name: Install dependencies - uses: wevm/actions/.github/actions/pnpm@main + uses: wevm/actions/.github/actions/pnpm@f7ad7f00e16e73322562922c241f21f0c7ffbbec + with: + node-version: 24.5 - name: Report build size - uses: preactjs/compressed-size-action@v2 + uses: preactjs/compressed-size-action@946a292cd35bd1088e0d7eb92b69d1a8d5b5d76a with: pattern: 'packages/**/dist/**' repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..e1b1ae75a9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,52 @@ +name: Release + +permissions: + contents: write + pull-requests: write + +on: + push: + branches: + - main + +jobs: + release: + name: Release + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [19] + pnpm-version: [8] + steps: + - uses: actions/checkout@v3 + with: + # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits + fetch-depth: 0 + submodules: true + + - uses: pnpm/action-setup@v2.2.4 + with: + version: ${{ matrix.pnpm-version }} + - name: Set up Node ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + cache: 'pnpm' + node-version: ${{ matrix.node-version }} + - name: Install Dependencies + run: pnpm i + + - name: Create Release Pull Request or Publish to npm + id: changesets + uses: changesets/action@v1.4.1 + with: + title: 'chore: version packages' + commit: 'chore: version packages' + version: pnpm changeset:version + publish: pnpm changeset:release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Publish CJS bundle to npm + if: steps.changesets.outputs.published == 'true' + run: pnpm cjs:release '${{ steps.changesets.outputs.publishedPackages }}' diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000000..ba176318ec --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,51 @@ +name: Scorecard +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '22 13 * * 0' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled. + if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request' + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + + steps: + - name: "Checkout code" + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + + - name: "Upload artifact" + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@80cb6b56b93de3e779c7d476d9100d06fb87c877 # codeql-bundle-v2.23.2 + with: + sarif_file: results.sarif diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml deleted file mode 100644 index 39683bb684..0000000000 --- a/.github/workflows/snapshot.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Snapshot -on: - workflow_dispatch: - -jobs: - snapshot: - name: Release snapshot version - permissions: - contents: write - id-token: write - runs-on: ubuntu-latest - timeout-minutes: 5 - - steps: - - name: Clone repository - uses: actions/checkout@v4 - - - name: Install dependencies - uses: wevm/actions/.github/actions/pnpm@main - - - name: Publish Snapshots - continue-on-error: true - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - snapshot=$(git branch --show-current | tr -cs '[:alnum:]-' '-' | tr '[:upper:]' '[:lower:]' | sed 's/-$//') - npm config set "//registry.npmjs.org/:_authToken" "$NPM_TOKEN" - pnpm clean - pnpm changeset version --no-git-tag --snapshot $snapshot - pnpm changeset:prepublish - pnpm changeset publish --no-git-tag --snapshot $snapshot --tag $snapshot diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 2ec609c42c..f9473414da 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -2,6 +2,8 @@ name: Verify on: workflow_call: workflow_dispatch: +permissions: + contents: read jobs: check: @@ -13,12 +15,17 @@ jobs: steps: - name: Clone repository - uses: actions/checkout@v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 with: token: ${{ secrets.GH_PTOKEN }} - name: Install dependencies - uses: wevm/actions/.github/actions/pnpm@main + uses: wevm/actions/.github/actions/pnpm@f7ad7f00e16e73322562922c241f21f0c7ffbbec + with: + node-version: 24.5 + + - name: Audit dependencies + run: pnpm audit - name: Check repo run: pnpm check:repo @@ -29,7 +36,7 @@ jobs: - name: Update package versions run: pnpm version:update - - uses: stefanzweifel/git-auto-commit-action@v5 + - uses: stefanzweifel/git-auto-commit-action@778341af668090896ca464160c2def5d1d1a3eb0 # v6.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -39,19 +46,23 @@ jobs: build: name: Build + permissions: + contents: read needs: check runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Clone repository - uses: actions/checkout@v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 - name: Install dependencies - uses: wevm/actions/.github/actions/pnpm@main + uses: wevm/actions/.github/actions/pnpm@f7ad7f00e16e73322562922c241f21f0c7ffbbec + with: + node-version: 24.5 - name: Build - run: pnpm build + run: pnpm clean && pnpm build - name: Publint run: pnpm test:build @@ -61,20 +72,24 @@ jobs: types: name: Types + permissions: + contents: read needs: check runs-on: ubuntu-latest timeout-minutes: 5 strategy: matrix: - typescript-version: ['5.2.2', '5.3.3', '5.4.5', '5.5.2'] - viem-version: ['2.23.12', 'latest'] + typescript-version: ['5.7.3', '5.8.3', '5.9.3', 'latest'] + viem-version: ['2.46.0', 'latest'] steps: - name: Clone repository - uses: actions/checkout@v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 - name: Install dependencies - uses: wevm/actions/.github/actions/pnpm@main + uses: wevm/actions/.github/actions/pnpm@f7ad7f00e16e73322562922c241f21f0c7ffbbec + with: + node-version: 24.5 - run: pnpm add -D -w typescript@${{ matrix.typescript-version }} viem@${{ matrix.viem-version }} @@ -84,6 +99,9 @@ jobs: - name: Check types run: pnpm check:types + - name: Bench types + run: pnpm bench:types + # Redundant with `pnpm check:types` # If Vitest adds special features in the future, e.g. type coverage, can add this back! # - name: Test types @@ -91,28 +109,49 @@ jobs: test: name: Test + permissions: + contents: read runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 20 strategy: - max-parallel: 3 + max-parallel: 4 matrix: - shard: [1, 2, 3] - total-shards: [3] + shard: [1, 2, 3, 4] + total-shards: [4] steps: - name: Clone repository - uses: actions/checkout@v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 - name: Install dependencies - uses: wevm/actions/.github/actions/pnpm@main + uses: wevm/actions/.github/actions/pnpm@f7ad7f00e16e73322562922c241f21f0c7ffbbec + with: + node-version: 24.5 + + - name: Setup Docker + uses: docker/setup-docker-action@v4 - name: Set up foundry - uses: foundry-rs/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@50d5a8956f2e319df19e6b57539d7e2acb9f8c1e # v1.5.0 with: version: nightly + - name: Cache Playwright + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb + id: playwright-cache + with: + path: ~/.cache/ms-playwright + key: ${{ runner.os }}-playwright-${{ hashFiles('pnpm-lock.yaml') }} + + - name: Install Playwright + if: steps.playwright-cache.outputs.cache-hit != 'true' + run: pnpm exec playwright install chromium --with-deps + + - name: Pull Tempo Docker image + run: docker pull ghcr.io/tempoxyz/tempo:latest + - name: Run tests - uses: nick-fields/retry@v3 + uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 with: command: CI=true pnpm test:cov --shard=${{ matrix.shard }}/${{ matrix.total-shards }} --retry=3 --bail=1 max_attempts: 3 @@ -122,6 +161,6 @@ jobs: VITE_OPTIMISM_FORK_URL: ${{ secrets.VITE_OPTIMISM_FORK_URL }} - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/vhs.yml b/.github/workflows/vhs.yml new file mode 100644 index 0000000000..372c5672c9 --- /dev/null +++ b/.github/workflows/vhs.yml @@ -0,0 +1,79 @@ +name: vhs + +on: + push: + branches: + - main + paths: + - packages/**/*.tape + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + VHS: true + +jobs: + vhs: + name: vhs + permissions: + contents: write + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20] + pnpm-version: [9] + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - uses: pnpm/action-setup@v2.2.4 + with: + version: ${{ matrix.pnpm-version }} + - name: Set up Node ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + cache: 'pnpm' + node-version: ${{ matrix.node-version }} + + - name: Cache pnpm + uses: actions/cache@v3 + with: + path: ~/.pnpm-store + key: pnpm-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: pnpm- + - name: Cache node_modules + uses: actions/cache@v3 + with: + path: | + node_modules + docs/node_modules + examples/**/node_modules + packages/**/node_modules + references/packages/**/node_modules + key: modules-${{ hashFiles('pnpm-lock.yaml') }} + - name: Install Dependencies + run: pnpm i + + # TODO: Combine these steps once supported + # https://github.com/charmbracelet/vhs-action/issues/50 + - uses: charmbracelet/vhs-action@v1 + with: + path: 'packages/cli/vhs/init.tape' + - uses: charmbracelet/vhs-action@v1 + with: + path: 'packages/cli/vhs/generate.tape' + env: + ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }} + + - uses: stefanzweifel/git-auto-commit-action@v4 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + commit_message: 'chore: update vhs' + commit_user_name: 'github-actions[bot]' + commit_user_email: 'github-actions[bot]@users.noreply.github.com' + file_pattern: '*.gif *.mp4' diff --git a/.gitignore b/.gitignore index 1834fe72ab..93fce5ccc7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,17 @@ +*.vitest-temp.json .DS_Store +.attest .next .nuxt .pnpm-debug.log* +.tanstack +.wrangler +_ cache coverage dist node_modules tsconfig.tsbuildinfo -*.vitest-temp.json # local env files .env @@ -26,14 +30,25 @@ packages/core/codegen packages/core/experimental packages/core/internal packages/core/query +packages/core/tempo packages/react/actions packages/react/chains packages/react/codegen packages/react/connectors packages/react/experimental +packages/react/internal packages/react/query +packages/react/tempo +packages/solid/actions +packages/solid/chains +packages/solid/connectors +packages/solid/internal +packages/solid/query packages/vue/actions packages/vue/chains packages/vue/connectors +packages/vue/internal packages/vue/nuxt packages/vue/query +.vercel +.env*.local diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..6c786c571f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "references"] + path = references + url = https://github.com/wagmi-dev/references.git diff --git a/.npmrc b/.npmrc index 47687565bf..dcf42f4997 100644 --- a/.npmrc +++ b/.npmrc @@ -1,5 +1,4 @@ auto-install-peers=false enable-pre-post-scripts=true link-workspace-packages=deep -provenance=true strict-peer-dependencies=false diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..7e8a4896b3 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,11 @@ +node_modules +dist +generated +CHANGELOG.md + +// Once prettier fixes MDX ignore ranges +// https://github.com/prettier/prettier/pull/12208 +docs/pages/index.*.mdx +docs/pages/*/getting-started.en-US.mdx +docs/pages/*/module-types.en-US.mdx +docs/pages/cli/create-wagmi.en-US.mdx diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..db48a0af6e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "arrowParens": "always", + "endOfLine": "lf", + "printWidth": 80, + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all" +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 9cb435094a..9a23ed995d 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,3 @@ { - "recommendations": [ - "biomejs.biome", - "orta.vscode-twoslash-queries", - "Vue.volar" - ] + "recommendations": ["dbaeumer.vscode-eslint", "orta.vscode-twoslash-queries"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index c32e8fa4c3..6318c082c0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,8 +5,7 @@ "typescript.preferences.importModuleSpecifier": "shortest", "typescript.tsdk": "node_modules/typescript/lib", "editor.codeActionsOnSave": { - "quickfix.biome": "explicit", - "source.organizeImports.biome": "explicit" + "source.fixAll.biome": "explicit" }, "[javascript][javascriptreact][json][typescript][typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" diff --git a/.vscode/workspace.code-workspace b/.vscode/workspace.code-workspace deleted file mode 100644 index 0d626129da..0000000000 --- a/.vscode/workspace.code-workspace +++ /dev/null @@ -1,16 +0,0 @@ -{ - "folders": [ - { - "name": "docs", - "path": "../docs" - }, - { - "name": "packages", - "path": "../packages" - }, - { - "name": "playgrounds", - "path": "../playgrounds" - } - ] -} diff --git a/FUNDING.json b/FUNDING.json deleted file mode 100644 index 5e01254162..0000000000 --- a/FUNDING.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "drips": { - "ethereum": { - "ownedBy": "0xd2135CfB216b74109775236E36d4b433F1DF507B" - } - }, - "opRetro": { - "projectId": "0xc0615947773148cbc340b175fb9afc98dbb4e0acd31d018b1ee41a5538785abf" - } -} diff --git a/LICENSE b/LICENSE index 650c3c1c00..bac2027ae9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022-present weth, LLC +Copyright (c) 2023-present weth, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md new file mode 100644 index 0000000000..03e6554c80 --- /dev/null +++ b/README.md @@ -0,0 +1,237 @@ +<p align="center"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/logo-dark.svg"> + <img alt="wagmi logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/logo-light.svg" width="auto" height="60"> + </picture> +</p> + +<p align="center"> + React Hooks for Ethereum +<p> + +<p align="center"> + <a href="https://www.npmjs.com/package/wagmi"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/npm/v/wagmi?colorA=21262d&colorB=21262d&style=flat"> + <img src="https://img.shields.io/npm/v/wagmi?colorA=f6f8fa&colorB=f6f8fa&style=flat" alt="Version"> + </picture> + </a> + <a href="https://github.com/wagmi-dev/wagmi/blob/main/LICENSE"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/npm/l/wagmi?colorA=21262d&colorB=21262d&style=flat"> + <img src="https://img.shields.io/npm/l/wagmi?colorA=f6f8fa&colorB=f6f8fa&style=flat" alt="MIT License"> + </picture> + </a> + <a href="https://www.npmjs.com/package/wagmi"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/npm/dm/wagmi?colorA=21262d&colorB=21262d&style=flat"> + <img src="https://img.shields.io/npm/dm/wagmi?colorA=f6f8fa&colorB=f6f8fa&style=flat" alt="Downloads per month"> + </picture> + </a> + <a href="https://bestofjs.org/projects/wagmi"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/endpoint?colorA=21262d&colorB=21262d&style=flat&url=https://bestofjs-serverless.now.sh/api/project-badge?fullName=wagmi-dev%2Fviem%26since=daily"> + <img src="https://img.shields.io/endpoint?colorA=f6f8fa&colorB=f6f8fa&style=flat&url=https://bestofjs-serverless.now.sh/api/project-badge?fullName=wagmi-dev%2Fviem%26since=daily" alt="Best of JS"> + </picture> + </a> +</p> + +<br> + +> **Note** +> +> wagmi is in the Gitcoin Grants Beta Round until May 9. [Click here to support development.](https://explorer.gitcoin.co/#/round/1/0xdf22a2c8f6ba9376ff17ee13e6154b784ee92094/0xdf22a2c8f6ba9376ff17ee13e6154b784ee92094-4) Thank you 🙏 + +## Features + +- 🚀 20+ hooks for working with wallets, ENS, contracts, transactions, signing, etc. +- 💼 Built-in wallet connectors for MetaMask, WalletConnect, Coinbase Wallet, Injected, and more +- 👟 Caching, request deduplication, multicall, batching, and persistence +- 🌀 Auto-refresh data on wallet, block, and network changes +- 🦄 TypeScript ready (infer types from ABIs and EIP-712 Typed Data) +- 📦 Command-line interface for managing ABIs and code generation +- 🌳 Test suite running against forked Ethereum network + +...and a lot more. + +## Documentation + +For full documentation and examples, visit [wagmi.sh](https://wagmi.sh). + +## Installation + +Install wagmi and its ethers peer dependency. + +```bash +npm install wagmi ethers@^5 +``` + +## Quick Start + +Connect a wallet in under 60 seconds. LFG. + +```tsx +import { WagmiConfig, createClient } from 'wagmi' +import { getDefaultProvider } from 'ethers' + +const client = createClient({ + autoConnect: true, + provider: getDefaultProvider(), +}) + +function App() { + return ( + <WagmiConfig client={client}> + <Profile /> + </WagmiConfig> + ) +} +``` + +```tsx +import { useAccount, useConnect, useDisconnect } from 'wagmi' +import { InjectedConnector } from 'wagmi/connectors/injected' + +function Profile() { + const { address } = useAccount() + const { connect } = useConnect({ + connector: new InjectedConnector(), + }) + const { disconnect } = useDisconnect() + + if (address) + return ( + <div> + Connected to {address} + <button onClick={() => disconnect()}>Disconnect</button> + </div> + ) + return <button onClick={() => connect()}>Connect Wallet</button> +} +``` + +In this example, we create a wagmi `Client` and pass it to the `WagmiConfig` React Context. The client is set up to use the ethers Default Provider and automatically connect to previously connected wallets. + +Next, we use the `useConnect` hook to connect an injected wallet (e.g. MetaMask) to the app. Finally, we show the connected account's address with `useAccount` and allow them to disconnect with `useDisconnect`. + +We've only scratched the surface for what you can do with wagmi! + +— + +Check out [ConnectKit](https://docs.family.co/connectkit?utm_source=wagmi-dev) or [Web3Modal](https://web3modal.com) to get started with pre-built interface on top of wagmi for managing wallet connections. + +## Community + +Check out the following places for more wagmi-related content: + +- Join the [discussions on GitHub](https://github.com/wagmi-dev/wagmi/discussions) +- Follow [@wagmi_sh](https://twitter.com/wagmi_sh) on Twitter for project updates +- Share [your project/organization](https://github.com/wagmi-dev/wagmi/discussions/201) using wagmi +- Browse the [awesome-wagmi](https://github.com/wagmi-dev/awesome-wagmi) list of awesome projects and resources + +## Support + +If you find wagmi useful, please consider supporting development. Thank you 🙏 + +- [GitHub Sponsors](https://github.com/sponsors/wagmi-dev?metadata_campaign=gh_readme_support) +- [Gitcoin Grant](https://wagmi.sh/gitcoin) +- [wagmi-dev.eth](https://etherscan.io/enslookup-search?search=wagmi-dev.eth) + +## Sponsors + +<a href="https://paradigm.xyz"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/paradigm-dark.svg"> + <img alt="paradigm logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/paradigm-light.svg" width="auto" height="70"> + </picture> +</a> + +<br> + +<a href="https://twitter.com/family"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/family-dark.svg"> + <img alt="family logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/family-light.svg" width="auto" height="50"> + </picture> +</a> +<a href="https://twitter.com/context"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/context-dark.svg"> + <img alt="context logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/context-light.svg" width="auto" height="50"> + </picture> +</a> +<a href="https://walletconnect.com"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/walletconnect-dark.svg"> + <img alt="WalletConnect logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/walletconnect-light.svg" width="auto" height="50"> + </picture> +</a> +<a href="https://looksrare.org"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/looksrare-dark.svg"> + <img alt="LooksRare logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/8923685e23fe9708b74d456c3f9e7a2b90f6abd9/content/sponsors/looksrare-light.svg" width="auto" height="50"> + </picture> +</a> +<a href="https://twitter.com/prtyDAO"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/partydao-dark.svg"> + <img alt="PartyDAO logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/partydao-light.svg" width="auto" height="50"> + </picture> +</a> +<a href="https://dynamic.xyz"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/dynamic-dark.svg"> + <img alt="Dynamic logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/dynamic-light.svg" width="auto" height="50"> + </picture> +</a> +<a href="https://sushi.com"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/sushi-dark.svg"> + <img alt="Sushi logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/sushi-light.svg" width="auto" height="50"> + </picture> +</a> +<a href="https://stripe.com"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/stripe-dark.svg"> + <img alt="Stripe logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/stripe-light.svg" width="auto" height="50"> + </picture> +</a> +<a href="https://bitkeep.com"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/bitkeep-dark.svg"> + <img alt="BitKeep logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/bitkeep-light.svg" width="auto" height="50"> + </picture> +</a> +<a href="https://www.privy.io"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/privy-dark.svg"> + <img alt="Privy logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/privy-light.svg" width="auto" height="50"> + </picture> +</a> +<a href="https://www.spruceid.com"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/spruce-dark.svg"> + <img alt="Spruce logo" src="https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/spruce-light.svg" width="auto" height="50"> + </picture> +</a> + +## Contributing + +If you're interested in contributing, please read the [contributing docs](/.github/CONTRIBUTING.md) **before submitting a pull request**. + +## Authors + +- [@tmm](https://github.com/tmm) (awkweb.eth, [Twitter](https://twitter.com/awkweb)) +- [@jxom](https://github.com/jxom) (moxey.eth, [Twitter](https://twitter.com/jakemoxey)) + +Thanks to julianhutton.eth ([@julianjhutton](https://twitter.com/julianjhutton)) for providing the awesome logo! + +## License + +[MIT](/LICENSE) License + +<br /> + +<a href="https://vercel.com/?utm_source=wagmi-dev&utm_campaign=oss"> + <img src="https://www.datocms-assets.com/31049/1618983297-powered-by-vercel.svg" alt="Powered by Vercel" height="35"> +</a> diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..034e848032 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,21 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 5.1.x | :white_check_mark: | +| 5.0.x | :x: | +| 4.0.x | :white_check_mark: | +| < 4.0 | :x: | + +## Reporting a Vulnerability + +Use this section to tell people how to report a vulnerability. + +Tell them where to go, how often they can expect to get an update on a +reported vulnerability, what to expect if the vulnerability is accepted or +declined, etc. diff --git a/biome.json b/biome.json index ce99662cb0..9fb7318f87 100644 --- a/biome.json +++ b/biome.json @@ -1,7 +1,14 @@ { "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", "files": { - "ignore": ["CHANGELOG.md", "pnpm-lock.yaml", "tsconfig.base.json"] + "includes": [ + "**", + "!**/CHANGELOG.md", + "!**/pnpm-lock.yaml", + "!**/routeTree.gen.ts", + "!**/tsconfig.base.json", + "!**/worker-configuration.d.ts" + ] }, "formatter": { "enabled": true, @@ -11,13 +18,16 @@ "lineWidth": 80 }, "linter": { - "ignore": ["packages/create-wagmi/templates/*"], + "includes": ["**", "!**/packages/create-wagmi/templates/**/*"], "enabled": true, "rules": { "recommended": true, "a11y": { "useButtonType": "off" }, + "complexity": { + "noImportantStyles": "off" + }, "correctness": { "noUnusedVariables": "error", "useExhaustiveDependencies": "error" @@ -29,13 +39,34 @@ }, "style": { "noNonNullAssertion": "off", - "useShorthandArrayType": "error" + "noParameterAssign": "error", + "useAsConstAssertion": "error", + "useDefaultParameterLast": "error", + "useEnumInitializers": "error", + "useSelfClosingElements": "error", + "useSingleVarDeclarator": "error", + "noUnusedTemplateLiteral": "error", + "useNumberNamespace": "error", + "noInferrableTypes": "error", + "noUselessElse": "error", + "useConsistentArrayType": { + "level": "error", + "options": { + "syntax": "shorthand" + } + } }, "suspicious": { "noArrayIndexKey": "off", "noConfusingVoidType": "off", - "noConsoleLog": "error", - "noExplicitAny": "off" + "noExplicitAny": "off", + "noConsole": { + "level": "error", + "options": { + "allow": ["log"] + } + }, + "noTsIgnore": "off" } } }, @@ -46,32 +77,52 @@ "semicolons": "asNeeded" } }, - "organizeImports": { - "enabled": true + "assist": { + "actions": { + "source": { + "organizeImports": "on" + } + } }, "overrides": [ { - "include": ["*.vue"], + "includes": ["**/*.vue"], "linter": { "rules": { "correctness": { + "noUnusedImports": "off", "noUnusedVariables": "off" } } } }, { - "include": ["./scripts/**/*.ts"], + "includes": ["site/snippets/**"], + "linter": { + "rules": { + "correctness": { + "noUnusedImports": "off" + } + } + } + }, + { + "includes": ["scripts/**/*.ts"], "linter": { "rules": { "suspicious": { - "noConsoleLog": "off" + "noConsole": { + "level": "off", + "options": { + "allow": ["log"] + } + } } } } }, { - "include": ["./playgrounds/**"], + "includes": ["playgrounds/**"], "linter": { "rules": { "style": { diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000..7a1bd50b7f --- /dev/null +++ b/codecov.yml @@ -0,0 +1,23 @@ +codecov: + notify: + wait_for_ci: true + require_ci_to_pass: true +comment: + behavior: default + layout: reach,diff,flags,tree,reach + show_carryforward_flags: false +coverage: + precision: 2 + range: + - 60.0 + - 80.0 + round: down + status: + changes: false + default_rules: + flag_coverage_not_uploaded_behavior: include + patch: true + project: true +github_checks: + annotations: true +slack_app: true diff --git a/docs/.env.example b/docs/.env.example new file mode 100644 index 0000000000..3e69067d5d --- /dev/null +++ b/docs/.env.example @@ -0,0 +1,4 @@ +NEXT_IRON_PASSWORD= +NEXT_PUBLIC_ALCHEMY_ID= +NEXT_PUBLIC_FATHOM_ID= +NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID= diff --git a/docs/components/blog/Authors.tsx b/docs/components/blog/Authors.tsx new file mode 100644 index 0000000000..e04b64e81e --- /dev/null +++ b/docs/components/blog/Authors.tsx @@ -0,0 +1,29 @@ +type AuthorsProps = { + date: string + children: React.ReactNode +} + +export function Authors({ date, children }: AuthorsProps) { + return ( + <div className="mt-4 mb-8 text-gray-400 text-sm"> + {date} by{children} + </div> + ) +} + +type AuthorProps = { name: string; link: string } + +export function Author({ name, link }: AuthorProps) { + return ( + <span className="after:content-[','] last:after:content-['']"> + <a + key={name} + href={link} + target="_blank" + className="mx-1 text-gray-800 dark:text-gray-100" + > + {name} + </a> + </span> + ) +} diff --git a/docs/components/blog/BlogIndex.tsx b/docs/components/blog/BlogIndex.tsx new file mode 100644 index 0000000000..55307c8655 --- /dev/null +++ b/docs/components/blog/BlogIndex.tsx @@ -0,0 +1,53 @@ +import Link from 'next/link' +import type { Page } from 'nextra' +// eslint-disable-next-line import/no-unresolved +import { getPagesUnderRoute } from 'nextra/context' + +export function BlogIndex({ more = 'Read more' }) { + return ( + <> + <h1 className="text-center font-extrabold text-3xl mb-10 md:text-5xl mt-10 md:mb-14"> + wagmi Blog + </h1> + + {getPagesUnderRoute('/blog').map( + ( + page: Page & { + frontMatter?: { + date?: string + description?: string + title?: string + } + }, + ) => { + return ( + <div className="mb-12"> + <h3 className="text-2xl font-bold mb-4"> + <Link + href={page.route} + style={{ color: 'inherit', textDecoration: 'none' }} + > + {page.meta?.title || page.frontMatter?.title || page.name} + </Link> + </h3> + + <p className="opacity-80 mb-3"> + {page.frontMatter?.description}{' '} + <Link + href={page.route} + className="nx-text-primary-500 underline" + > + {more + ' →'} + </Link> + </p> + + {page.frontMatter?.date ? ( + <p className="opacity-50 text-sm">{page.frontMatter.date}</p> + ) : null} + </div> + ) + }, + )} + </> + ) +} diff --git a/docs/components/blog/index.ts b/docs/components/blog/index.ts new file mode 100644 index 0000000000..c1e130136c --- /dev/null +++ b/docs/components/blog/index.ts @@ -0,0 +1,2 @@ +export { Authors, Author } from '../blog/Authors' +export { BlogIndex } from '../blog/BlogIndex' diff --git a/docs/components/core/LogoType.tsx b/docs/components/core/LogoType.tsx new file mode 100644 index 0000000000..4221895442 --- /dev/null +++ b/docs/components/core/LogoType.tsx @@ -0,0 +1,36 @@ +type Props = { + title?: string +} + +export function LogoType({ title = 'wagmi logo' }: Props) { + return ( + <svg + viewBox="0 0 561 132" + fill="none" + xmlns="http://www.w3.org/2000/svg" + className="fill-current h-full w-auto" + > + <title>{title} + + + + + + ) +} diff --git a/docs/components/core/PreviewWrapper.tsx b/docs/components/core/PreviewWrapper.tsx new file mode 100644 index 0000000000..e2b4372eef --- /dev/null +++ b/docs/components/core/PreviewWrapper.tsx @@ -0,0 +1,33 @@ +import { Box, useTheme } from 'degen' +import { useTheme as useNextThemes } from 'next-themes' +import * as React from 'react' + +type Props = { + children: React.ReactNode +} + +export function PreviewWrapper({ children }: Props) { + const { resolvedTheme } = useNextThemes() + const { setMode } = useTheme() + + React.useEffect(() => { + if (!resolvedTheme) return + if (resolvedTheme === 'system') return + setMode(resolvedTheme as any) + }, [resolvedTheme, setMode]) + + return ( + + {children} + + ) +} diff --git a/docs/components/core/Providers.tsx b/docs/components/core/Providers.tsx new file mode 100644 index 0000000000..5ba77d031a --- /dev/null +++ b/docs/components/core/Providers.tsx @@ -0,0 +1,57 @@ +import * as React from 'react' + +import { WagmiConfig, configureChains, createClient } from 'wagmi' +import { goerli, mainnet } from 'wagmi/chains' + +import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet' +import { InjectedConnector } from 'wagmi/connectors/injected' +import { MetaMaskConnector } from 'wagmi/connectors/metaMask' +import { WalletConnectConnector } from 'wagmi/connectors/walletConnect' + +import { alchemyProvider } from 'wagmi/providers/alchemy' + +const { chains, provider, webSocketProvider } = configureChains( + [mainnet, goerli], + [alchemyProvider({ apiKey: process.env.NEXT_PUBLIC_ALCHEMY_ID ?? '' })], +) + +const client = createClient({ + autoConnect: true, + connectors: [ + new MetaMaskConnector({ + chains, + options: { + UNSTABLE_shimOnConnectSelectAccount: true, + }, + }), + new CoinbaseWalletConnector({ + chains, + options: { + appName: 'wagmi', + }, + }), + new WalletConnectConnector({ + chains, + options: { + projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID ?? '', + }, + }), + new InjectedConnector({ + chains, + options: { + name: 'Injected', + shimDisconnect: true, + }, + }), + ], + provider, + webSocketProvider, +}) + +type Props = { + children?: React.ReactNode +} + +export function Providers({ children }: Props) { + return {children} +} diff --git a/docs/components/core/index.ts b/docs/components/core/index.ts new file mode 100644 index 0000000000..a5bb6b918a --- /dev/null +++ b/docs/components/core/index.ts @@ -0,0 +1,3 @@ +export { LogoType } from './LogoType' +export { PreviewWrapper } from './PreviewWrapper' +export { Providers } from './Providers' diff --git a/docs/components/docs/Header.tsx b/docs/components/docs/Header.tsx new file mode 100644 index 0000000000..1196d20690 --- /dev/null +++ b/docs/components/docs/Header.tsx @@ -0,0 +1,113 @@ +import { useRouter } from 'next/router' + +import { LogoType } from '../core' + +const TITLE_WITH_TRANSLATIONS: Record = { + 'en-US': 'React Hooks for Ethereum', +} + +export function Header() { + const { locale, defaultLocale = 'en-US' } = useRouter() + const resolvedLocale = locale || defaultLocale + const title = TITLE_WITH_TRANSLATIONS[resolvedLocale] + + return ( +
+
+

wagmi

+ + + + +
+ +

+ {title} +

+ + +
+ ) +} diff --git a/docs/components/docs/Sponsors.tsx b/docs/components/docs/Sponsors.tsx new file mode 100644 index 0000000000..e98cf39979 --- /dev/null +++ b/docs/components/docs/Sponsors.tsx @@ -0,0 +1,155 @@ +import { useTheme } from 'next-themes' + +const sponsors = [ + { + id: 'family', + name: 'Family', + href: 'https://twitter.com/family', + logo: { + dark: 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/family-dark.svg', + light: + 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/family-light.svg', + }, + }, + { + id: 'context', + name: 'Context', + href: 'https://twitter.com/context', + logo: { + dark: 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/context-dark.svg', + light: + 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/context-light.svg', + }, + }, + { + id: 'walletconnect', + name: 'WalletConnect', + href: 'https://walletconnect.com', + logo: { + dark: 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/walletconnect-dark.svg', + light: + 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/walletconnect-light.svg', + }, + }, + { + id: 'looksrare', + name: 'LooksRare', + href: 'https://looksrare.org', + logo: { + dark: 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/looksrare-dark.svg', + light: + 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/looksrare-light.svg', + }, + }, + { + id: 'partydao', + name: 'PartyDAO', + href: 'https://twitter.com/prtyDAO', + logo: { + dark: 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/partydao-dark.svg', + light: + 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/partydao-light.svg', + }, + }, + { + id: 'dynamic', + name: 'Dynamic', + href: 'https://www.dynamic.xyz', + logo: { + dark: 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/dynamic-dark.svg', + light: + 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/dynamic-light.svg', + }, + }, + { + id: 'sushi', + name: 'Sushi', + href: 'https://www.sushi.com', + logo: { + dark: 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/sushi-dark.svg', + light: + 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/sushi-light.svg', + }, + }, + { + id: 'stripe', + name: 'Stripe', + href: 'https://www.stripe.com', + logo: { + dark: 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/stripe-dark.svg', + light: + 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/stripe-light.svg', + }, + }, + { + id: 'bitkeep', + name: 'BitKeep', + href: 'https://bitkeep.com', + logo: { + dark: 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/bitkeep-dark.svg', + light: + 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/bitkeep-light.svg', + }, + }, + { + id: 'privy', + name: 'Privy', + href: 'https://privy.io', + logo: { + dark: 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/privy-dark.svg', + light: + 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/privy-light.svg', + }, + }, + { + id: 'spruce', + name: 'Spruce', + href: 'https://spruce.io', + logo: { + dark: 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/spruce-dark.svg', + light: + 'https://raw.githubusercontent.com/wagmi-dev/.github/main/content/sponsors/spruce-light.svg', + }, + }, +] as const + +export function Sponsors() { + const { resolvedTheme } = useTheme() + const mode = (resolvedTheme ?? 'dark') as 'dark' | 'light' + return ( +
+ +
+ {sponsors.map((sponsor) => ( + + + {sponsor.name} + + + ))} +
+
+ ) +} diff --git a/docs/components/docs/ValidationComparisonTable.tsx b/docs/components/docs/ValidationComparisonTable.tsx new file mode 100644 index 0000000000..f6749ee86c --- /dev/null +++ b/docs/components/docs/ValidationComparisonTable.tsx @@ -0,0 +1,47 @@ +export function ValidationComparisonTable() { + return ( + + + + + + + + + + + + + +
+ Validating after interaction + + Validating before interaction +
+
+
+
+
+
+
+ ) +} diff --git a/docs/components/docs/index.ts b/docs/components/docs/index.ts new file mode 100644 index 0000000000..3d5907b6cb --- /dev/null +++ b/docs/components/docs/index.ts @@ -0,0 +1,2 @@ +export { Header } from './Header' +export { Sponsors } from './Sponsors' diff --git a/docs/components/examples/ConnectWallet.tsx b/docs/components/examples/ConnectWallet.tsx new file mode 100644 index 0000000000..9c89cc478b --- /dev/null +++ b/docs/components/examples/ConnectWallet.tsx @@ -0,0 +1,21 @@ +import { useAccount } from 'wagmi' + +import { PreviewWrapper } from '../core' +import { Account, WalletSelector } from '../web3' + +export function ConnectWallet() { + const { isConnected } = useAccount() + + if (isConnected) + return ( + + + + ) + + return ( + + + + ) +} diff --git a/docs/components/examples/ContractWrite.tsx b/docs/components/examples/ContractWrite.tsx new file mode 100644 index 0000000000..fb067514cf --- /dev/null +++ b/docs/components/examples/ContractWrite.tsx @@ -0,0 +1,84 @@ +import { Button, Stack, Text } from 'degen' +import { + useAccount, + useContractWrite, + useNetwork, + usePrepareContractWrite, + useWaitForTransaction, +} from 'wagmi' + +import { PreviewWrapper } from '../core' +import { Account, SwitchNetwork, WalletSelector } from '../web3' + +export function ContractWrite() { + const { isConnected } = useAccount() + + const { + config, + error: prepareError, + isError: isPrepareError, + isLoading: isPreparing, + } = usePrepareContractWrite({ + address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', + abi: [ + { + name: 'mint', + type: 'function', + stateMutability: 'nonpayable', + inputs: [], + outputs: [], + }, + ], + functionName: 'mint', + }) + const { + data, + error, + isLoading: isWriteLoading, + isError: isWriteError, + write, + } = useContractWrite(config) + const { isLoading: isConfirming, isSuccess } = useWaitForTransaction({ + hash: data?.hash, + }) + const { chain } = useNetwork() + + if (isConnected) + return ( + + + + + {isPrepareError && ( + Error: {prepareError?.message} + )} + {isWriteError && Error: {error?.message}} + {isSuccess && ( + + Success!{' '} + + Etherscan + + + )} + + + + ) + + return ( + + + + ) +} diff --git a/docs/components/examples/ContractWriteDynamic.tsx b/docs/components/examples/ContractWriteDynamic.tsx new file mode 100644 index 0000000000..3d66bc0f57 --- /dev/null +++ b/docs/components/examples/ContractWriteDynamic.tsx @@ -0,0 +1,104 @@ +import { Button, Input, Stack, Text } from 'degen' +import * as React from 'react' +import { + useAccount, + useContractWrite, + useNetwork, + usePrepareContractWrite, + useWaitForTransaction, +} from 'wagmi' + +import { useDebounce } from '../../hooks' + +import { PreviewWrapper } from '../core' +import { Account, SwitchNetwork, WalletSelector } from '../web3' + +export function ContractWriteDynamic() { + const { isConnected } = useAccount() + + const [tokenId, setTokenId] = React.useState('') + const debouncedTokenId = useDebounce(tokenId) + + const { + config, + error: prepareError, + isError: isPrepareError, + isLoading: isPreparing, + } = usePrepareContractWrite({ + address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', + abi: [ + { + name: 'mint', + type: 'function', + stateMutability: 'nonpayable', + inputs: [{ internalType: 'uint32', name: 'tokenId', type: 'uint32' }], + outputs: [], + }, + ], + functionName: 'mint', + args: [parseInt(debouncedTokenId)], + enabled: Boolean(debouncedTokenId), + }) + const { + data, + error, + isLoading: isWriteLoading, + isError: isWriteError, + write, + } = useContractWrite(config) + const { isLoading: isConfirming, isSuccess } = useWaitForTransaction({ + hash: data?.hash, + }) + const { chain } = useNetwork() + + if (isConnected) + return ( + +
{ + e.preventDefault() + write?.() + }} + > + + + setTokenId(e.target.value)} + placeholder="69" + value={tokenId} + /> + + {isPrepareError && ( + Error: {prepareError?.message} + )} + {isWriteError && Error: {error?.message}} + {isSuccess && ( + + Success!{' '} + + Etherscan + + + )} + + +
+
+ ) + + return ( + + + + ) +} diff --git a/docs/components/examples/SendTransaction.tsx b/docs/components/examples/SendTransaction.tsx new file mode 100644 index 0000000000..fe236c9538 --- /dev/null +++ b/docs/components/examples/SendTransaction.tsx @@ -0,0 +1,111 @@ +import { Box, Button, Input, Stack, Text } from 'degen' +import { parseEther } from 'ethers/lib/utils' +import * as React from 'react' +import { + useAccount, + usePrepareSendTransaction, + useSendTransaction, + useWaitForTransaction, +} from 'wagmi' + +import { PreviewWrapper } from '../core' +import { Account, WalletSelector } from '../web3' + +function useDebounce(value: any, delay: number) { + const [debouncedValue, setDebouncedValue] = React.useState(value) + + React.useEffect(() => { + const handler = setTimeout(() => { + setDebouncedValue(value) + }, delay) + + return () => clearTimeout(handler) + }, [value, delay]) + + return debouncedValue +} + +export function SendTransaction() { + const { isConnected } = useAccount() + + const [to, setTo] = React.useState('') + const debouncedTo = useDebounce(to, 500) + + const [value, setValue] = React.useState('') + const debouncedValue = useDebounce(value, 500) + + const { + config, + error: prepareError, + isError: isPrepareError, + isLoading: isPreparing, + } = usePrepareSendTransaction({ + request: { + to: debouncedTo, + value: debouncedValue ? parseEther(debouncedValue) : undefined, + }, + }) + const { data, error, isLoading, isError, sendTransaction } = + useSendTransaction(config) + const { isLoading: isConfirming, isSuccess } = useWaitForTransaction({ + hash: data?.hash, + }) + + if (isConnected) + return ( + + { + e.preventDefault() + if (isLoading || isConfirming) return + sendTransaction?.() + }} + > + + + setTo(e.target.value)} + label="Recipient" + placeholder="0xA0Cf…251e" + value={to} + /> + setValue(e.target.value)} + label="Amount (ether)" + placeholder="0.05" + type="number" + value={value} + /> + + {isSuccess && ( + + Success!{' '} + Etherscan + + )} + {isPrepareError && ( + Error: {prepareError?.message} + )} + {isError && Error: {error?.message}} + + + + ) + + return ( + + + + ) +} diff --git a/docs/components/examples/SignInWithEthereum.tsx b/docs/components/examples/SignInWithEthereum.tsx new file mode 100644 index 0000000000..755e49a564 --- /dev/null +++ b/docs/components/examples/SignInWithEthereum.tsx @@ -0,0 +1,75 @@ +import { Box, Button, Skeleton, Stack } from 'degen' +import * as React from 'react' +import { mainnet, useAccount, useNetwork } from 'wagmi' + +import { formatAddress } from '../../lib/address' +import { PreviewWrapper } from '../core' +import { Account, SiweButton, WalletSelector } from '../web3' + +export function SignInWithEthereum() { + const [address, setAddress] = React.useState() + const accountData = useAccount() + const { chain: activeChain } = useNetwork() + + React.useEffect(() => { + const handler = async () => { + try { + const res = await fetch('/api/me') + const json = await res.json() + setAddress(json.address) + } catch (error) { + console.log({ error }) + } + } + handler() + + window.addEventListener('focus', handler) + return () => window.removeEventListener('focus', handler) + }, []) + + const signedInContent = address ? ( + + Signed in as {formatAddress(address)} + + + ) : null + + if (accountData.isConnected) + return ( + + + + + {address ? ( + signedInContent + ) : ( + + setAddress(address)} + /> + + )} + + + ) + + return ( + + + + {signedInContent} + + + ) +} diff --git a/docs/components/examples/SignMessage.tsx b/docs/components/examples/SignMessage.tsx new file mode 100644 index 0000000000..deb0968865 --- /dev/null +++ b/docs/components/examples/SignMessage.tsx @@ -0,0 +1,72 @@ +import { Box, Button, Text, Textarea } from 'degen' +import { verifyMessage } from 'ethers/lib/utils' +import * as React from 'react' +import { useAccount, useSignMessage } from 'wagmi' + +import { PreviewWrapper } from '../core' +import { Account, WalletSelector } from '../web3' + +export function SignMessage() { + const recoveredAddress = React.useRef() + + const { isConnected } = useAccount() + const { + data: signMessageData, + error, + isLoading, + signMessage, + } = useSignMessage({ + onSuccess(data, variables) { + const address = verifyMessage(variables.message, data) + recoveredAddress.current = address + }, + }) + + if (isConnected) + return ( + + + { + event.preventDefault() + const formData = new FormData(event.target as HTMLFormElement) + const message = formData.get('message') as string + signMessage({ message }) + }} + > +